﻿#region << 版 本 注 释 >>
/*----------------------------------------------------------------
 * 版权所有 (c) 2022 北京超维景生物科技有限公司 保留所有权利。
 * 
 * 创建者：huangyang
 * 电子邮箱：huangyang@tvscope.cn
 * 创建时间：2023/2/23 16:12:49
 * 版本：V1.0.0
 * 描述：
 *
 * ----------------------------------------------------------------
 * 修改人：
 * 时间：
 * 修改说明：
 *
 * 版本：V1.0.1
 *----------------------------------------------------------------*/
#endregion << 版 本 注 释 >>

using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Text;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using System.Xml.Linq;
using ImageK;
using ImageK.Gui;
using ImageK.IO;
using ImageK.Macros;
using ImageK.Measure;
using ImageK.Plugin.Filter;
using ImageK.Plugin.Frame;
using ImageK.Plugin;
using ImageK.Process;
using ImageK.Util;
using Microsoft.VisualBasic.ApplicationServices;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using ImageK.Java;
using System.IO;
using System.IO.Compression;
using Button = System.Windows.Forms.Button;

namespace ImageK.Plugin.Frame
{
    public class RoiManager : PlugInFrame
    {
        public static string LOC_KEY = "manager.loc";

        private static String MULTI_CROP_DIR = "multi-crop.dir";
        private static int BUTTONS = 11;
        private static int DRAW = 0, FILL = 1, LABEL = 2;
        private static int SHOW_ALL = 0, SHOW_NONE = 1, LABELS = 2, NO_LABELS = 3;
        private static int MENU = 0, COMMAND = 1;
        private static int IGNORE_POSITION = -999;  // ignore the ROI's built in position
        private static int CHANNEL = 0, SLICE = 1, FRAME = 2, SHOW_DIALOG = 3;
        private static int rows = 15;
        private static int lastNonShiftClick = -1;
        private static bool allowMultipleSelections = true;
        private static String moreButtonLabel = "More "+'\u00bb';
        private static Form instance;

        private static int colorIndex = 4;
        private ListBox list;
        private List<string> listModel;

        private ArrayList rois = new ArrayList();
        private bool canceled;
        private bool macro;
        private bool ignoreInterrupts;

        private ContextMenuStrip pm;
        private Button moreButton, colorButton;
        private CheckBox showAllCheckbox = new CheckBox
        {
            Text = "Show All",
            Checked = false,
        };

        private CheckBox labelsCheckbox = new CheckBox
        {
            Text = "Labels",
            Checked = false,
        };
        private Overlay overlayTemplate;

        private static bool measureAll = true;
        private static bool onePerSlice = true;
        private static bool restoreCentered;
        private int prevID;
        private bool noUpdateMode;
        private int defaultLineWidth = 1;
        private Color defaultColor;
        private bool firstTime = true;
        private bool appendResults;
        private static ResultsTable mmResults, mmResults2;
        private int imageID;
        private bool allowRecording;
        private bool recordShowAll = true;
        private bool allowDuplicates;
        private double translateX = 10.0;
        private double translateY = 10.0;
        private static String errorMessage;
        private bool multiCropShow = true;
        private bool multiCropSave;
        private int multiCropFormatIndex;
        private static double angle = 45.0;
        private static double xscale = 1.5;
        private static double yscale = 1.5;
        private FlowLayoutPanel panel;
        private System.ComponentModel.IContainer components;
        private static bool scaleCentered = true;

        /** Opens the "ROI Manager" window, or activates it if it is already open.
         * @see #RoiManager(boolean)
         * @see #getRoiManager
        */
        public RoiManager()
        {
            Text = "ROI Manager";
            if (instance!=null)
            {
                WindowManager.toFront(instance);
                return;
            }
            if (IJ.isMacro() && Interpreter.getBatchModeRoiManager()!=null)
            {
                list = new ListBox();
                listModel = new List<string>();
                list.DataSource = listModel;
                return;
            }
            instance = this;
            //todo:
            // list = new JList();
            errorMessage = null;

            InitializeComponent();
            showWindow();
        }

        /** Constructs an ROI Manager without displaying it. The boolean argument is ignored. */
        public RoiManager(bool b)
        {
            Text = "ROI Manager";
            // list = new JList();
            // listModel = new DefaultListModel();
            // list.setModel(listModel);
            // errorMessage = null;
        }

        private void InitializeComponent()
        {
            components=new System.ComponentModel.Container();
            list=new ListBox();
            panel=new FlowLayoutPanel();
            pm=new ContextMenuStrip(components);
            SuspendLayout();
            // 
            // list
            // 
            list.Dock=DockStyle.Fill;
            list.FormattingEnabled=true;
            list.ItemHeight=17;
            list.Location=new Point(0, 0);
            list.Name="list";
            list.Size=new Size(297, 334);
            list.TabIndex=0;
            // 
            // panel
            // 
            panel.Dock=DockStyle.Right;
            panel.FlowDirection=FlowDirection.TopDown;
            panel.Location=new Point(222, 0);
            panel.Margin=new Padding(0);
            panel.Name="panel";
            panel.Size=new Size(75, 334);
            panel.TabIndex=1;
            // 
            // pm
            // 
            pm.Name="pm";
            pm.Size=new Size(181, 26);
            // 
            // RoiManager
            // 
            ClientSize=new Size(297, 334);
            Controls.Add(panel);
            Controls.Add(list);
            Name="RoiManager";
            ResumeLayout(false);
        }

        void showWindow()
        {
            ImageK ij = IJ.GetInstance();
            this.KeyPress += (sender, args) =>
            KeyDown+= ij.keyPressed;
            //todo:
            // addKeyListener(ij);
            // addMouseListener(this);
            // addMouseWheelListener(this);
            WindowManager.addWindow(this);
            //setLayout(new FlowLayout(FlowLayout.CENTER,5,5));
            // setLayout(new BorderLayout());
            // listModel = new DefaultListModel();
            // list.setModel(listModel);
            // GUI.scale(list);
            list.SelectedIndexChanged += valueChanged;
            // list.setPrototypeCellValue("0000-0000-0000 ");
            // list.addListSelectionListener(this);
            // list.addKeyListener(ij);
            // list.addMouseListener(this);
            // list.addMouseWheelListener(this);
            // if (IJ.isLinux()) list.setBackground(Color.white);
            // JScrollPane scrollPane = new JScrollPane(list, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
            // add("Center", scrollPane);
            // panel = new Panel();
            // int nButtons = BUTTONS;
            // panel.setLayout(new GridLayout(nButtons, 1, 5, 0));
            addButton("Add [t]");
            addButton("Update");
            addButton("Delete");
            addButton("Rename...");
            addButton("Measure");
            addButton("Deselect");
            addButton("Properties...");
            addButton("Flatten [F]");
            addButton(moreButtonLabel);
            // showAllCheckbox.addItemListener(this);
            panel.Controls.Add(showAllCheckbox);
            // labelsCheckbox.addItemListener(this);
            panel.Controls.Add(labelsCheckbox);
            // add("East", panel);
            addPopupMenu();
            // GUI.scale(this);
            // pack();
            // Dimension size = getSize();
            // if (size.width>270)
            //     setSize(size.width-40, size.height);
            // list.remove(0);
            // Point loc = Prefs.getLocation(LOC_KEY);
            // if (loc!=null)
            //     setLocation(loc);
            // else
            //     GUI.centerOnImageJScreen(this);
            Show();
        }

        void addButton(string label)
        {
            Button b = new Button
            {
                Text = label,
                Margin = new Padding(0)
            };
            b.Click += actionPerformed;
            // b.addActionListener(this);
            // b.addKeyListener(IJ.getInstance());
            // b.addMouseListener(this);
            if (label.Equals(moreButtonLabel)) moreButton = b;
            panel.Controls.Add(b);
        }

        void addPopupMenu()
        {
            addPopupItem("Open...");
            addPopupItem("Save...");
            addPopupItem("Fill");
            addPopupItem("Draw");
            addPopupItem("AND");
            addPopupItem("OR (Combine)");
            addPopupItem("XOR");
            addPopupItem("Split");
            addPopupItem("Add Particles");
            addPopupItem("Multi Measure");
            addPopupItem("Multi Plot");
            addPopupItem("Multi Crop");
            addPopupItem("Sort");
            addPopupItem("Specify...");
            addPopupItem("Remove Positions...");
            addPopupItem("Labels...");
            addPopupItem("List");
            addPopupItem("Interpolate ROIs");
            addPopupItem("Scale...");
            addPopupItem("Rotate...");
            addPopupItem("Translate...");
            addPopupItem("ROI Manager Action");
            addPopupItem("Help");
            addPopupItem("Options...");
        }

        void addPopupItem(String s)
        {
            ToolStripMenuItem mi = new ToolStripMenuItem(s);
            mi.Click += actionPerformed;
            pm.Items.Add(mi);
        }

        public void actionPerformed(object? sender, EventArgs e)
        {
            string label = "";
            if (sender is Button btn)
            {
                label = btn.Text;
            }
            else if (sender is ToolStripMenuItem menuItem)
            {
                label = menuItem.Text;
            }

            if (string.IsNullOrEmpty(label))
            {
                return;
            }

            string command = label;
            allowRecording = true;
            if (command.Equals("Add [t]"))
                runCommand("add");
            else if (command.Equals("Update"))
                update(true);
            else if (command.Equals("Delete"))
                delete(false);
            //           else if (command.equals("Rename..."))
            //               rename(null);
            //           else if (command.equals("Properties..."))
            //               setProperties(null, -1, null);
            //           else if (command.equals("Flatten [F]"))
            //               flatten();
            //           else if (command.equals("Measure"))
            //               measure(getImage());
            else if (command.Equals("Open..."))
                open(null);
            else if (command.Equals("Save..."))
            {
                // new Task(() =>
                // {
                //     save(null);
                // }).Start();
                save(null);
            }
            // else if (command.equals("Fill"))
            // 	drawOrFill(FILL);
            // else if (command.equals("Draw"))
            // 	drawOrFill(DRAW);
            // else if (command.equals("Deselect"))
            // 	deselect();
            else if (command.Equals(moreButtonLabel))
            {
                Point ploc = PointToScreen(panel.Location);
                Point bloc = PointToScreen(moreButton.Location);
                pm.Show(ploc.X, bloc.Y + moreButton.Height);
            }
            // else if (command.equals("OR (Combine)")) {
            // 	new MacroRunner("roiManager(\"Combine\");");
            // 	if (Recorder.record) Recorder.record("roiManager", "Combine");
            //   } else if (command.equals("Split"))
            // 	split();
            // else if (command.equals("AND"))
            // 	and();
            // else if (command.equals("XOR"))
            // 	xor();
            // else if (command.equals("Add Particles"))
            // 	addParticles();
            // else if (command.equals("Multi Measure"))
            // 	multiMeasure("");
            // else if (command.equals("Multi Plot"))
            // 	multiPlot();
            // else if (command.equals("Multi Crop"))
            // 	multiCrop();
            // else if (command.equals("Sort"))
            // 	sort();
            // else if (command.equals("Specify..."))
            // 	specify();
            // else if (command.equals("Remove Positions..."))
            // 	removePositions(SHOW_DIALOG);
            // else if (command.equals("Labels..."))
            // 	labels();
            // else if (command.equals("List"))
            // 	listRois();
            // else if (command.equals("Interpolate ROIs"))
            // 	interpolateRois();
            // else if (command.equals("Scale..."))
            // 	scale();
            // else if (command.equals("Rotate..."))
            // 	rotate();
            // else if (command.equals("Translate..."))
            // 	translate();
            // else if (command.equals("Help"))
            // 	help();
            // else if (command.equals("Options..."))
            // 	options();
            // else if (command.equals("\"Show All\" Color..."))
            // 	setShowAllColor();
            // else if (command.equals("ROI Manager Action"))
            // 			IJ.run(command);
            // allowRecording = false;
        }

        /** Returns a reference to the ROI Manager, or null if it is not open
         * and a batch mode macro is not running. If the ROI Manager
         * is not open and a batch mode macro is running,
         * returns the hidden batch mode RoiManager.
         * @see #getRoiManager
        */
        public static RoiManager getInstance()
        {
            if (instance == null && IJ.isMacro())
                return Interpreter.getBatchModeRoiManager();
            else
                return (RoiManager)instance;
        }



        public static RoiManager getRawInstance()
        {
            return (RoiManager)instance;
        }

//         private void interpolateRois()
//         {
//             IJ.runPlugIn("ij.plugin.RoiInterpolator", "");
//             if (record())
//                 Recorder.record("roiManager", "Interpolate ROIs");
//         }
//
//         public void itemStateChanged(ItemEvent e)
//         {
//             Object source = e.getSource();
//             boolean showAllMode = showAllCheckbox.getState();
//             if (source==showAllCheckbox)
//             {
//                 if (firstTime && okToSet())
//                     labelsCheckbox.setState(true);
//                 showAll(showAllCheckbox.getState() ? SHOW_ALL : SHOW_NONE);
//                 if (Recorder.record && recordShowAll)
//                 {
//                     if (showAllMode)
//                         Recorder.record("roiManager", "Show All");
//                     else
//                         Recorder.record("roiManager", "Show None");
//                 }
//                 recordShowAll = true;
//                 firstTime = false;
//                 return;
//             }
//             if (source==labelsCheckbox)
//             {
//                 if (firstTime && okToSet())
//                     showAllCheckbox.setState(true);
//                 boolean editState = labelsCheckbox.getState();
//                 boolean showAllState = showAllCheckbox.getState();
//                 if (!showAllState && !editState)
//                     showAll(SHOW_NONE);
//                 else
//                 {
//                     showAll(editState ? LABELS : NO_LABELS);
//                     if (Recorder.record)
//                     {
//                         if (editState)
//                             Recorder.record("roiManager", "Show All with labels");
//                         else if (showAllState)
//                             Recorder.record("roiManager", "Show All without labels");
//                     }
//                     if (editState && !showAllState && okToSet())
//                     {
//                         showAllCheckbox.setState(true);
//                         recordShowAll = false;
//                     }
//                 }
//                 firstTime = false;
//                 return;
//             }
//         }
//
         private bool okToSet()
         {
             //return !(IJ.isMacOSX()&&IJ.isJava18());
             return false;
         }

         void add(bool shiftKeyDown, bool altKeyDown)
         {
             if (shiftKeyDown)
             {
                 // addAndDraw(altKeyDown);
             }
             else if (altKeyDown)
                 addRoi(true);
             else
                 addRoi(false);
         }

         /** Adds the specified ROI. */
         public void addRoi(Roi roi)
         {
             allowDuplicates = true;
             addRoi(roi, false, Color.Empty, -1);
         }

         bool addRoi(bool promptForName)
         {
             return addRoi(null, promptForName, Color.Empty, IGNORE_POSITION);
         }

        bool addRoi(Roi roi, bool needPromptForName, Color color, int lineWidth)
        {
            if (list==null)
                IJ.log("<<Error: Uninitialized RoiManager>>");
            ImagePlus imp = roi==null ? getImage() : WindowManager.getCurrentImage();
            if (roi==null)
            {
                if (imp==null)
                    return false;
                roi = imp.getRoi();
                if (roi==null)
                {
                    error("The active image does not have a selection.");
                    return false;
                }
            }
            if ((roi is PolygonRoi) && ((PolygonRoi)roi).getNCoordinates()==0)
			    return false;
            if (color==Color.Empty && roi.getStrokeColor()!=Color.Empty)
                color = roi.getStrokeColor();
            else if (color==Color.Empty && defaultColor!=Color.Empty)
                color = defaultColor;
            bool ignorePosition = false;
            if (lineWidth==IGNORE_POSITION)
            {
                ignorePosition = true;
                lineWidth = -1;
            }
            if (lineWidth<0)
            {
                int sw = (int)roi.getStrokeWidth();
                lineWidth = sw>1 ? sw : defaultLineWidth;
            }
            if (lineWidth>100) lineWidth = 1;
            int n = getCount();
            int position = imp!=null&&!ignorePosition ? roi.getPosition() : 0;
            int saveCurrentSlice = imp!=null ? imp.getCurrentSlice() : 0;
            if (position>0 && position!=saveCurrentSlice)
            {
                //todo:
        //         if (imp.lock())
				    // imp.setSliceWithoutUpdate(position);
        //         else
        //             return false;   //can't lock image, must not change the stack slice
            }
            else
                position = 0;       //we need to revert to the original stack slice and unlock if position>0
            if (n>0 && !IJ.isMacro() && imp!=null && !allowDuplicates)
            {
                // check for duplicate
                Roi roi2 = (Roi)rois[n-1];
                if (roi2!=null)
                {
                    string labl = (string)list.Items[n-1];
                    int slice2 = getSliceNumber(roi2, labl);
                    if (roi.Equals(roi2) && (slice2==-1||slice2==imp.getCurrentSlice()) && imp.getID()==prevID && !Interpreter.isBatchMode())
                    {
                        if (position>0)
                        {
                            //todo:
                            // imp.setSliceWithoutUpdate(saveCurrentSlice);
                            // imp.unlock();
                        }
                        return false;
                    }
                }
            }
            allowDuplicates = false;
            prevID = imp!=null ? imp.getID() : 0;
            string name = roi.getName();
            if (isStandardName(name))
                name = null;
            string label = name!=null ? name : getLabel(imp, roi, -1);
            if (needPromptForName)
                label = promptForName(label);
            if (label==null)
            {
                if (position>0)
                {
                    //todo:
                    // imp.setSliceWithoutUpdate(saveCurrentSlice);
                    // imp.unlock();
                }
                return false;
            }
            list.Items.Add(label);
            roi.setName(label);
            Roi roiCopy = (Roi)roi.clone();
            if (ignorePosition && imp!=null && imp.getStackSize()>1 && imp.getWindow()!=null && this.Visible)
            {
                // set ROI position to current stack position if image and RoiManager are visible
                roiCopy.setPosition(imp);
            }
            if (lineWidth>1)
                roiCopy.setStrokeWidth(lineWidth);
            if (color!=null)
                roiCopy.setStrokeColor(color);
            rois.Add(roiCopy);
            updateShowAll();
            if (record())
            {
                //todo：
                // recordAdd(defaultColor, defaultLineWidth);
            }
                
            if (position>0)
            {
                //todo:
                // imp.setSliceWithoutUpdate(saveCurrentSlice);
                // imp.unlock();
            }
            return true;
        }
//
//         void recordAdd(Color color, int lineWidth)
//         {
//             if (Recorder.scriptMode())
//                 Recorder.recordCall("rm.addRoi(roi);");
//             else if (color!=null && lineWidth==1)
//                 Recorder.recordString("roiManager(\"Add\", \""+getHex(color)+"\");\n");
//             else if (lineWidth>1)
//                 Recorder.recordString("roiManager(\"Add\", \""+getHex(color)+"\", "+lineWidth+");\n");
//             else
//                 Recorder.record("roiManager", "Add");
//         }
//
//         String getHex(Color color)
//         {
//             if (color==null) color = ImageCanvas.getShowAllColor();
//             String hex = Integer.toHexString(color.getRGB());
//             if (hex.length()==8) hex = hex.substring(2);
//             return hex;
//         }
//
//         /** Adds the specified ROI to the list. The second argument ('n') will
//          * be used to form the first part of the ROI label if it is zero or greater.
//          * @param roi		the Roi to be added
//          * @param n		if zero or greater, will be used to form the first part of the label
//         */
//         public void add(Roi roi, int n)
//         {
//             add((ImagePlus)null, roi, n);
//         }
//
//         /** Adds the specified ROI to the list. The third argument ('n') will
//          * be used to form the first part of the ROI label if it is zero or greater.
//          * @param imp	the image associated with the ROI, or null
//          * @param roi		the Roi to be added
//          * @param n		if zero or greater, will be used to form the first part of the label
//         */
//         public void add(ImagePlus imp, Roi roi, int n)
//         {
//             if (IJ.debugMode && n<3 && roi!=null) IJ.log("RoiManager.add: "+n+" "+roi.getName());
//             if (roi==null)
//                 return;
//             String label = roi.getName();
//             String label2 = label;
//             if (label==null)
//                 label = getLabel(imp, roi, n);
//             else
//             {
//                 if (n>=0)
//                     label = n+"-"+label;
//             }
//             if (label==null)
//                 return;
//             listModel.addElement(label);
//             if (label2!=null)
//                 roi.setName(label2);
//             else
//                 roi.setName(label);
//             rois.add((Roi)roi.clone());
//         }
//
//         /** Replaces the ROI at the specified index. */
//         public void setRoi(Roi roi, int index)
//         {
//             if (index<0 || index>=rois.size())
//                 throw new IllegalArgumentException("setRoi: Index out of range");
//             rois.set(index, (Roi)roi.clone());
//             updateShowAll();
//         }
//
        bool isStandardName(string name)
        {
             if (string.IsNullOrEmpty(name))
                 return false;
             int len = name.Length;
             if (len<9 || (len>0 && !Char.IsDigit(name[0])))
                 return false;
             bool isStandard = false;
             if (len>=14 && name[4]=='-' && name[9]=='-')
                 isStandard = true;
             else if (len>=17 && name[5]=='-' && name[11]=='-')
                 isStandard = true;
             else if (len>=9 && name[4]=='-' && Char.IsDigit(name[5]))
                 isStandard = true;
             else if (len>=11 && name[5]=='-' && Char.IsDigit(name[6]))
                 isStandard = true;
             return isStandard;
        }

        string getLabel(ImagePlus imp, Roi roi, int n)
        {
             Rectangle r = roi.getBounds();
             int xc = r.X + r.Width/2;
             int yc = r.Y + r.Height/2;
             if (n>=0)
             { xc = yc; yc=n; }
             if (xc<0) xc = 0;
             if (yc<0) yc = 0;
             int digits = 4;
             string xs = "" + xc;
             if (xs.Length>digits) digits = xs.Length;
             string ys = "" + yc;
             if (ys.Length>digits) digits = ys.Length;
             if (digits==4 && imp!=null && (imp.getStackSize()>=10000||imp.getHeight()>=10000))
                 digits = 5;
             xs = "000000" + xc;
             ys = "000000" + yc;
             string label = ys.Substring(ys.Length-digits) + "-" + xs.Substring(xs.Length-digits);
             if (imp!=null && imp.getStackSize()>1)
             {
                 int slice = imp.getCurrentSlice();
                 string zs = "000000" + slice;
                 label = zs.Substring(zs.Length-digits) + "-" + label;
             }
             return label;
        }
//
//         void addAndDraw(boolean altKeyDown)
//         {
//             if (altKeyDown)
//             {
//                 if (!addRoi(true)) return;
//             }
//             else if (!addRoi(false))
//                 return;
//             ImagePlus imp = WindowManager.getCurrentImage();
//             if (imp!=null)
//             {
//                 Undo.setup(Undo.COMPOUND_FILTER, imp);
//                 IJ.run(imp, "Draw", "slice");
//                 Undo.setup(Undo.COMPOUND_FILTER_DONE, imp);
//             }
//             if (record()) Recorder.record("roiManager", "Add & Draw");
//         }
//
         bool delete(bool replacing)
         {
             int count = getCount();
             if (count==0)
                 return error("The ROI Manager is empty.");
             int[] index = getSelectedIndexes();
             if (index.Length==0 || (replacing&&count>1))
             {
                 String msg = "Delete all items on the list?";
                 if (replacing)
                     msg = "Replace items on the list?";
                 canceled = false;
                 if (!IJ.isMacro() && !macro)
                 {
                     // YesNoCancelDialog d = new YesNoCancelDialog(this, "ROI Manager", msg);
                     // if (d.cancelPressed())
                     // { canceled = true; return false; }
                     // if (!d.yesPressed()) return false;
                     var dResult = MessageBox.Show(msg, "ROI Manager", MessageBoxButtons.YesNoCancel);
                     if (dResult == DialogResult.Cancel)
                     {
                         { canceled = true; return false; }
                    }

                     if (dResult != DialogResult.Yes)
                         return false;
                 }
                 index = getAllIndexes();
             }
             if (count==index.Length && !replacing)
             {
                 rois.Clear();
                 list.Items.Clear();
             }
             else
             {
                 for (int i = count-1; i>=0; i--)
                 {
                     bool delete = false;
                     for (int j = 0; j<index.Length; j++)
                     {
                         if (index[j]==i)
                             delete = true;
                     }
                     if (delete)
                     {
                         // if (EventQueue.isDispatchThread())
                         // {
                         rois.Remove(i);
                         list.Items.RemoveAt(i);
                         // }
                         // else
                         //     deleteOnEDT(i);
                     }
                 }
             }
             ImagePlus imp = WindowManager.getCurrentImage();
             updateShowAll();
             //todo:
             // if (record())
             //     Recorder.record("roiManager", "Delete");
             return true;
         }
//
//         // Delete ROI on event dispatch thread
//         private void deleteOnEDT(final int i)
//         {
//             try
//             {
//                 EventQueue.invokeAndWait(new Runnable()
//                 {
//
//                 public void run()
//                 {
//                     rois.remove(i);
//                     listModel.remove(i);
//                 }
//             });
//         } catch (
//             Exception e) {
// 		}
// }
//
        bool update(bool clone)
        {
            ImagePlus imp = getImage();
            if (imp==null)
                return false;
            ImageCanvas ic = imp.getCanvas();
            bool showingAll = ic!=null &&  ic.getShowAllROIs();
            Roi roi = imp.getRoi();
            if (roi==null)
            {
                error("The active image does not have a selection.");
                return false;
            }
            int index = list.SelectedIndex;
            if (index<0 && !showingAll)
                return error("Exactly one item in the list must be selected.");
            if (index>=0)
            {
                if (clone)
                {
                    string name = (string)list.Items[index];
                    Roi roi2 = (Roi)roi.clone();
                    roi2.setPosition(imp);
                    roi.setName(name);
                    roi2.setName(name);
                    rois[index] = roi2;
                }
                else
                    rois[index] = roi;
            }
            // if (record()) Recorder.record("roiManager", "Update");
            updateShowAll();
            return true;
        }
//
// boolean rename(String name2)
// {
//     int index = list.getSelectedIndex();
//     if (index<0)
//         return error("Exactly one item in the list must be selected.");
//     String name = (String)listModel.getElementAt(index);
//     if (name2==null)
//         name2 = promptForName(name);
//     if (name2==null)
//         return false;
//     if (name2.equals(name))
//         return false;
//     Roi roi = (Roi)rois.get(index);
//     roi.setName(name2);
//     int position = getSliceNumber(name2);
//     if (position>0 && !roi.hasHyperStackPosition())
//         roi.setPosition(position);
//     rois.set(index, roi);
//     listModel.setElementAt(name2, index);
//     list.setSelectedIndex(index);
//     if (Prefs.useNamesAsLabels && labelsCheckbox.getState())
//     {
//         ImagePlus imp = WindowManager.getCurrentImage();
//         if (imp!=null) imp.draw();
//     }
//     if (record())
//         Recorder.record("roiManager", "Rename", name2);
//     return true;
// }
//
// public void rename(int index, String newName)
// {
//     if (index<0 || index>=getCount())
//         throw new IllegalArgumentException("Index out of range: "+index);
//     Roi roi = (Roi)rois.get(index);
//     roi.setName(newName);
//     listModel.setElementAt(newName, index);
// }
//
        string promptForName(string name)
        {
            GenericDialog gd = new GenericDialog("ROI Manager");
            gd.addStringField("Rename As:", name, 20);
            gd.showDialog();
            if (gd.wasCanceled())
                return null;
            else
                return gd.getNextString();
        }

        bool restore(ImagePlus imp, int index, bool setSlice)
        {
            Roi roi = (Roi)rois[index];
            if (imp==null || roi==null)
                return false;
            //IJ.log("restore: "+roi.getPosition()+"  "+roi.getZPosition()+"  "+imp.getNSlices()+"  "+imp.getStackSize());
            if (setSlice)
            {
                bool hyperstack = imp.isHyperStack();
                int position = roi.getPosition();
                if (hyperstack && roi.hasHyperStackPosition())
                    imp.setPosition(roi.getCPosition(), roi.getZPosition(), roi.getTPosition());
                else if (hyperstack && imp.getNSlices()==1)
                    imp.setPosition(imp.getChannel(), 1, position);
                else if (hyperstack)
                    imp.setPosition(imp.getChannel(), position, imp.getChannel());
                else if (roi.getZPosition()>0 && imp.getNSlices()==imp.getStackSize())
                    imp.setSlice(roi.getZPosition());
                else if (position>0 && position<=imp.getStackSize())
                    imp.setSlice(position);
                else
                {
                    string label = (string)list.Items[index];
                    int n = getSliceNumber(roi, label);
                    if (n>=1 && n<=imp.getStackSize())
                    {
                        if (hyperstack)
                        {
                            if (imp.getNSlices()>1 && n<=imp.getNSlices())
                                imp.setPosition(imp.getC(), n, imp.getT());
                            else if (imp.getNFrames()>1 && n<=imp.getNFrames())
                                imp.setPosition(imp.getC(), imp.getZ(), n);
                            else
                                imp.setPosition(n);
                        }
                        else
                            imp.setSlice(n);
                    }
                }
            }
            if (showAllCheckbox.Checked && !restoreCentered && !noUpdateMode)
            {
                roi.setImage(null);
                imp.setRoi(roi);
                return true;
            }
            Roi roi2 = (Roi)roi.clone();
            Rectangle r = roi2.getBounds();
            int width = imp.getWidth(), height = imp.getHeight();
            if (restoreCentered)
            {
                ImageCanvas ic = imp.getCanvas();
                if (ic!=null)
                {
                    Rectangle r1 = ic.getSrcRect();
                    Rectangle r2 = roi2.getBounds();
                    roi2.setLocation(r1.X+r1.Width/2-r2.Width/2, r1.Y+r1.Height/2-r2.Height/2);
                }
            }
            if (r.X>=width || r.Y>=height || (r.X+r.Width)<0 || (r.Y+r.Height)<0)
            {
                if (roi2.getType()!=Roi.POINT)
                    roi2.setLocation((width-r.Width)/2, (height-r.Height)/2);
            }
            if (noUpdateMode)
            {
                imp.setRoi(roi2, false);
                noUpdateMode = false;
            }
            else
                imp.setRoi(roi2, true);
            return true;
        }

        private bool restoreWithoutUpdate(ImagePlus imp, int index)
        {
            noUpdateMode = true;
            if (imp==null)
                imp = getImage();
            return restore(imp, index, false);
        }

        /** Returns the slice number associated with the specified name,
            or -1 if the name does not include a slice number. */
        public int getSliceNumber(string label)
        {
            int slice = -1;
            if (label.Length>=14 && label[4]=='-' && label[9]=='-')
                slice = (int)Tools.parseDouble(label.Substring(0, 4), -1);
            else if (label.Length>=17 && label[5]=='-' && label[11]=='-')
                slice = (int)Tools.parseDouble(label.Substring(0, 5), -1);
            else if (label.Length>=20 && label[6]=='-' && label[13]=='-')
                slice = (int)Tools.parseDouble(label.Substring(0, 6), -1);
            return slice;
        }

        /** Returns the slice number associated with the specified ROI or name,
            or -1 if the ROI or name does not include a slice number. */
        int getSliceNumber(Roi roi, string label)
        {
            int slice = roi!=null ? roi.getPosition() : -1;
            if (slice==0)
                slice=-1;
            if (slice==-1)
                slice = getSliceNumber(label);
            return slice;
        }

        /** Opens a single .roi file or a ZIP-compressed set of ROIs.
         *	Returns 'true' if the operation was succesful.
        */
        public bool open(string path)
        {
            Macro.setOptions(null);
            string name = null;
            if (path==null || path.Equals(""))
            {
                OpenDialog od = new OpenDialog("Open Selection(s)...", "");
                String directory = od.GetDirectory();
                name = od.GetFileName();
                if (name==null)
                    return false;
                path = directory + name;
            }
            if (Recorder.OnRecord && !IJ.MacroRunning())
            {
                //todo:
                // if (Recorder.scriptMode())
                //     Recorder.recordCall("rm.open(\""+path+"\");");
                // else
                //     Recorder.record("roiManager", "Open", path);
            }
            bool ok = false;
            if (path.EndsWith(".zip"))
            {
                bool wasRecording = Recorder.OnRecord;
                Recorder.OnRecord = false;
                ok = openZip(path);
                Recorder.OnRecord = wasRecording;
                return ok;
            }
            Opener o = new Opener();
            if (name==null) name = o.getName(path);
            Roi roi = o.openRoi(path);
            if (roi!=null)
            {
                if (roi.getName()!=null)
                    name = roi.getName();
                if (name.EndsWith(".roi"))
                    name = name.Substring(0, name.Length-4);
                list.Items.Add(name);
                rois.Add(roi);
                errorMessage = null;
                ok = true;
            }
            else
            {
                errorMessage = "Unable to open ROI at "+path;
                ok = false;
            }
            updateShowAll();
            return ok;
        }

        // Modified on 2005/11/15 by Ulrik Stervbo to only read .roi files and to not empty the current list
        bool openZip(string path)
        {
            int nRois = 0;
            errorMessage = null;
            try
            {
                using FileStream fileStream = new FileStream(path, FileMode.Open);
                using ZipArchive zipArchive = new ZipArchive(fileStream, ZipArchiveMode.Read);
                byte[] buf = new byte[1024];
                foreach (ZipArchiveEntry entry in zipArchive.Entries)
                {
                    string name = entry.FullName;
                    if (name.EndsWith(".roi"))
                    {
                        using MemoryStream outStream = new MemoryStream();
                        using (Stream entryStream = entry.Open())
                        {
                            int len;
                            while ((len = entryStream.Read(buf, 0, buf.Length)) > 0)
                            {
                                outStream.Write(buf, 0, len);
                            }
                        }
                        outStream.Close();
                        byte[] bytes = outStream.ToArray();
                        RoiDecoder rd = new RoiDecoder(bytes, name);
                        Roi roi = rd.getRoi();
                        if (roi!=null)
                        {
                            name = name.Substring(0, name.Length-4);
                            list.Items.Add(name);
                            rois.Add(roi);
                            nRois++;
                        }
				    }
                }
		    } 
            catch (IOException e) 
            {
                errorMessage = e.Message;
                error(errorMessage);
            } 
            if (nRois==0 && errorMessage==null)
            {
                errorMessage = "This ZIP archive does not contain \".roi\" files: " + path;
                error(errorMessage);
            }
            updateShowAll();
            return errorMessage==null;
	    }

        /** If one ROI is selected, it is saved as a .roi
         * file, if multiple (or no) ROIs are selected,
         * they are saved as a .zip ROI set. Returns 
         * 'true' if the save operation was succesful.
         * @see #setSelectedIndexes
        */
        public bool save(string path)
        {
            if (getCount()==0)
                return error("The selection list is empty.");
            int[] indexes = getIndexes();
            if (indexes.Length>1)
                return saveMultiple(indexes, path);
            else
                return saveOne(indexes, path);

        }

        bool saveOne(int[] indexes, String path)
        {
            if (indexes.Length==0)
                return error("The list is empty");
            Roi roi = (Roi)rois[indexes[0]];
            if (path==null)
            {
                Macro.setOptions(null);
                string name = (string)list.Items[indexes[0]];
                SaveDialog sd = new SaveDialog("Save Selection...", name, ".roi");
                string name2 = sd.getFileName();
                if (name2 == null)
                    return false;
                string dir = sd.getDirectory();
                if (!name2.EndsWith(".roi")) name2 = name2+".roi";
                string newName = name2.Substring(0, name2.Length-4);
                rois[indexes[0]] = roi;
                roi.setName(newName);
                list.Items[indexes[0]] = newName;
                path = dir+name2;
            }
            RoiEncoder re = new RoiEncoder(path);
            errorMessage = null;
            try
            {
                re.write(roi);
            }
            catch (IOException e)
            {
                errorMessage = e.Message;
                IJ.error("ROI Manager", errorMessage);
            }
            //todo:
            // if (Recorder.record && !IJ.isMacro())
            // {
            //     if (Recorder.scriptMode())
            //         Recorder.recordCall("rm.save(\""+path+"\");");
            //     else
            //         Recorder.record("roiManager", "Save", path);
            // }
            return true;
        }
        
        bool saveMultiple(int[] indexes, String path)
        {
             Macro.setOptions(null);
             if (path==null || path.Equals(""))
             {
                  SaveDialog sd = new SaveDialog("Save ROIs...", "RoiSet", ".zip");
                  string name = sd.getFileName();
                  if (name == null)
                      return false;
                  if (!(name.EndsWith(".zip") || name.EndsWith(".ZIP")))
                      name = name + ".zip";
                  string dir = sd.getDirectory();
                  path = dir+name;
             }

             IJ.ShowStatus("Saving "+indexes.Length+" ROIs "+" to "+path);
             long t0 = DateTime.Now.Ticks;
             string[] names = new string[list.Items.Count];
             for (int i = 0; i<list.Items.Count; i++)
                 names[i] = (string)list.Items[i];
             errorMessage = null;
             try
             {
                 using FileStream fileStream = new FileStream(path, FileMode.Create);
                 using ZipArchive zipArchive = new ZipArchive(fileStream, ZipArchiveMode.Create);

                 for (int i = 0; i<indexes.Length; i++)
                 {
                     IJ.showProgress(i, indexes.Length);
                     string label = getUniqueName(names, indexes[i]);
                     Roi roi = (Roi)rois[indexes[i]];
                     if (IJ.debugMode) IJ.log("saveMultiple: "+i+"  "+label+"  "+roi);
                     if (roi==null) continue;
                     if (!label.EndsWith(".roi")) label += ".roi";
                     ZipArchiveEntry entry = zipArchive.CreateEntry(label);
                     using Stream entryStream = entry.Open();
                     using BinaryWriter writer = new BinaryWriter(entryStream);
                     RoiEncoder re = new RoiEncoder(writer);
                     re.write(roi);
                 }
             }
             catch (IOException e)
             {
                 errorMessage = ""+e;
                 error(errorMessage);
                 return false;
             }
             double time = (DateTime.Now.Ticks-t0)/1000.0;
             IJ.showProgress(1.0);
             IJ.ShowStatus(IJ.d2s(time, 3)+" seconds, "+indexes.Length+" ROIs, "+path);
             //todo:
             // if (Recorder.record && !IJ.isMacro())
             // {
             //     if (Recorder.scriptMode())
             //         Recorder.recordCall("rm.save(\""+path+"\");");
             //     else
             //         Recorder.record("roiManager", "Save", path);
             // }
            return true;
        }
        
        string getUniqueName(string[] names, int index)
        {
            string name = names[index];
            int n = 1;
            int index2 = getIndex(names, index, name);
            while (index2!=-1)
            {
                index2 = getIndex(names, index, name);
                if (index2!=-1)
                {
                    int lastDash = name.LastIndexOf("-");
                    if (lastDash!=-1 && name.Length-lastDash<5)
                        name = name.Substring(0, lastDash);
                    name = name+"-"+n;
                    n++;
                }
                index2 = getIndex(names, index, name);
            }
            names[index] = name;
            return name;
        }
        
        private int getIndex(string[] names, int index, string name)
        {
            int index2 = -1;
            for (int i = 0; i<names.Length; i++)
            {
                if (i!=index && names[i].Equals(name))
                    return i;
            }
            return index2;
        }
        //
        //         private void listRois()
        //         {
        //             Roi[] list = getRoisAsArray();
        //             OverlayCommands.listRois(list);
        //             if (record())
        //                 Recorder.record("roiManager", "List");
        //         }
        //
        //         boolean measure(ImagePlus imp)
        //         {
        //             if (imp==null)
        //                 return false;
        //             int[] indexes = getIndexes();
        //             if (indexes.length==0) return false;
        //             boolean allSliceOne = true;
        //             if (imp.getStackSize() > 1) // do we have to change the stack slice for one of the rois?
        //                 for (int i = 0; i<indexes.length; i++)
        //                 {
        //                     Roi roi = (Roi)rois.get(indexes[i]);
        //                     String label = (String)listModel.getElementAt(indexes[i]);
        //                     if (getSliceNumber(roi, label)>1 || roi.hasHyperStackPosition())
        //                     {
        //                         allSliceOne=false;
        //                         break;
        //                     }
        //                 }
        //             if (!allSliceOne)
        //                 if (!imp.lock ()) return false; // if we can't lock, we must not change the stack slice
        //             int measurements = Analyzer.getMeasurements();
        //             if (imp.getStackSize()>1)
        //                 Analyzer.setMeasurements(measurements|Measurements.SLICE);
        //             int currentSlice = imp.getCurrentSlice();
        //             Analyzer.setMeasurements(measurements&(~Measurements.ADD_TO_OVERLAY));
        //             for (int i = 0; i<indexes.length; i++)
        //             {
        //                 noUpdateMode = true;
        //                 if (restore(imp, indexes[i], !allSliceOne))
        //                     IJ.run(imp, "Measure", "");
        //                 else
        //                     break;
        //             }
        //             Analyzer.setMeasurements(measurements);
        //             imp.setSlice(currentSlice);
        //             if (indexes.length>1)
        //                 imp.deleteRoi();
        //             if (record()) Recorder.record("roiManager", "Measure");
        //             if (!allSliceOne)
        //                 imp.unlock();
        //             return true;
        //         }
        //
        //         /** This method measures the selected ROIs, or all ROIs if
        //          * none are selected, on all the slices of a stack and returns
        //          * a ResultsTable arranged with one row per slice.
        //          * @see <a href="http://imagej.nih.gov/ij/macros/js/MultiMeasureDemo.js">JavaScript example</a>
        //         */
        //         public ResultsTable multiMeasure(ImagePlus imp)
        //         {
        //             Roi[] rois = getSelectedRoisAsArray();
        //             ResultsTable rt = multiMeasure(imp, rois, false);
        //             imp.deleteRoi();
        //             return rt;
        //         }
        //
        //         /** This method performs measurements for several ROI's in a stack
        //             and arranges the results with one line per slice.  By contrast, the
        //             measure() method produces several lines per slice.	The results
        //             from multiMeasure() may be easier to import into a spreadsheet
        //             program for plotting or additional analysis. Based on the multi()
        //             method in Bob Dougherty's Multi_Measure plugin
        //             (http://www.optinav.com/Multi-Measure.htm).
        //         */
        //         boolean multiMeasure(String cmd)
        //         {
        //             ImagePlus imp = getImage();
        //             if (imp==null) return false;
        //             int[] indexes = getIndexes();
        //             if (indexes.length==0)
        //             {
        //                 error("Multi-measure: no selection");
        //                 return false;
        //             }
        //             if (!imp.lock ())
        //    return false;
        //             int measurements = Analyzer.getMeasurements();
        //
        //             int nSlices = imp.getStackSize();
        //             if (cmd!=null)
        //                 appendResults = cmd.contains("append") ? true : false;
        //             if (IJ.isMacro())
        //             {
        //                 if (cmd.startsWith("multi-measure"))
        //                 {
        //                     measureAll = cmd.contains(" measure") && nSlices>1; // measure-all
        //                     onePerSlice = cmd.contains(" one");
        //                     appendResults = cmd.contains(" append");
        //                 }
        //                 else
        //                 {
        //                     if (nSlices>1)
        //                         measureAll = true;
        //                     onePerSlice = true;
        //                 }
        //             }
        //             else
        //             {
        //                 GenericDialog gd = new GenericDialog("Multi Measure");
        //                 if (nSlices>1)
        //                     gd.addCheckbox("Measure all "+nSlices+" slices", measureAll);
        //                 gd.addCheckbox("One row per slice", onePerSlice);
        //                 gd.addCheckbox("Append results", appendResults);
        //                 int columns = getColumnCount(imp, measurements)*indexes.length;
        //                 String str = nSlices==1 ? "this option" : "both options";
        //                 gd.setInsets(10, 25, 0);
        //                 gd.addMessage(
        //                     "Enabling "+str+" will result\n"+
        //                     "in a table with "+columns+" columns."
        //                 );
        //                 gd.showDialog();
        //                 if (gd.wasCanceled())
        //                 {
        //                     imp.unlock();
        //                     return false;
        //                 }
        //                 if (nSlices>1)
        //                     measureAll = gd.getNextBoolean();
        //                 onePerSlice = gd.getNextBoolean();
        //                 appendResults = gd.getNextBoolean();
        //             }
        //             if (!measureAll) nSlices = 1;
        //             int currentSlice = imp.getCurrentSlice();
        //
        //             if (!onePerSlice)
        //             {
        //                 int measurements2 = nSlices>1 ? measurements|Measurements.SLICE : measurements;
        //                 ResultsTable rt = new ResultsTable();
        //                 rt.showRowNumbers(true);
        //                 if (appendResults && mmResults2!=null)
        //                     rt = mmResults2;
        //                 Analyzer analyzer = new Analyzer(imp, measurements2, rt);
        //                 analyzer.disableReset(true);
        //                 for (int slice = 1; slice<=nSlices; slice++)
        //                 {
        //                     if (nSlices>1) imp.setSliceWithoutUpdate(slice);
        //                     for (int i = 0; i<indexes.length; i++)
        //                     {
        //                         if (restoreWithoutUpdate(imp, indexes[i]))
        //                             analyzer.measure();
        //                         else
        //                             break;
        //                     }
        //                 }
        //                 mmResults2 = (ResultsTable)rt.clone();
        //                 rt.show("Results");
        //                 if (nSlices>1)
        //                     imp.setSlice(currentSlice);
        //             }
        //             else
        //             {
        //                 Roi[] rois = getSelectedRoisAsArray();
        //                 if ("".equals(cmd))
        //                 { // run More>>Multi Measure command in separate thread
        //                     MultiMeasureRunner mmr = new MultiMeasureRunner();
        //                     mmr.multiMeasure(imp, rois, appendResults);
        //                 }
        //                 else
        //                 {
        //                     ResultsTable rtMulti = multiMeasure(imp, rois, appendResults);
        //                     mmResults = (ResultsTable)rtMulti.clone();
        //                     rtMulti.show("Results");
        //                     imp.setSlice(currentSlice);
        //                     if (indexes.length>1)
        //                         IJ.run("Select None");
        //                 }
        //             }
        //             if (record())
        //             {
        //                 if (Recorder.scriptMode())
        //                 {
        //                     Recorder.recordCall("rt = rm.multiMeasure(imp);");
        //                     Recorder.recordCall("rt.show(\"Results\");");
        //                 }
        //                 else
        //                 {
        //                     if ((nSlices==1||measureAll) && onePerSlice && !appendResults)
        //                         Recorder.record("roiManager", "Multi Measure");
        //                     else
        //                     {
        //                         String options = "";
        //                         if (measureAll)
        //                             options += " measure_all";
        //                         if (onePerSlice)
        //                             options += " one";
        //                         if (appendResults)
        //                             options += " append";
        //                         Recorder.record("roiManager", "multi-measure"+options);
        //                     }
        //                 }
        //             }
        //             imp.unlock();
        //             return true;
        //         }
        //
        //         public static ResultsTable multiMeasure(ImagePlus imp, Roi[] rois, boolean appendResults)
        //         {
        //             int nSlices = imp.getStackSize();
        //             Analyzer aSys = new Analyzer(imp); // System Analyzer
        //             ResultsTable rtSys = Analyzer.getResultsTable();
        //             ResultsTable rtMulti = new ResultsTable();
        //             rtMulti.showRowNumbers(true);
        //             if (appendResults && mmResults!=null)
        //                 rtMulti = mmResults;
        //             rtSys.reset();
        //             int currentSlice = imp.getCurrentSlice();
        //             for (int slice = 1; slice<=nSlices; slice++)
        //             {
        //                 int sliceUse = slice;
        //                 if (nSlices==1) sliceUse = currentSlice;
        //                 imp.setSliceWithoutUpdate(sliceUse);
        //                 rtMulti.incrementCounter();
        //                 if ((Analyzer.getMeasurements()&Measurements.LABELS)!=0)
        //                     rtMulti.addLabel("Label", imp.getTitle());
        //                 int roiIndex = 0;
        //                 for (int i = 0; i<rois.length; i++)
        //                 {
        //                     imp.setRoi(rois[i]);
        //                     roiIndex++;
        //                     aSys.measure();
        //                     for (int j = 0; j<=rtSys.getLastColumn(); j++)
        //                     {
        //                         float[] col = rtSys.getColumn(j);
        //                         String head = rtSys.getColumnHeading(j);
        //                         String suffix = ""+roiIndex;
        //                         Roi roi = imp.getRoi();
        //                         if (roi!=null)
        //                         {
        //                             String name = roi.getName();
        //                             if (name!=null && name.length()>0 && (name.length()<9||!Character.isDigit(name.charAt(0))))
        //                                 suffix = "("+name+")";
        //                         }
        //                         if (head!=null && col!=null && !head.equals("Slice"))
        //                             rtMulti.addValue(head+suffix, rtSys.getValue(j, rtSys.getCounter()-1));
        //                     }
        //                 }
        //                 if (nSlices>1) IJ.showProgress(slice, nSlices);
        //             }
        //             return rtMulti;
        //         }
        //
        //         int getColumnCount(ImagePlus imp, int measurements)
        //         {
        //             ImageStatistics stats = imp.getStatistics(measurements);
        //             ResultsTable rt = new ResultsTable();
        //             rt.showRowNumbers(true);
        //             Analyzer analyzer = new Analyzer(imp, measurements, rt);
        //             analyzer.saveResults(stats, null);
        //             int count = 0;
        //             for (int i = 0; i<=rt.getLastColumn(); i++)
        //             {
        //                 float[] col = rt.getColumn(i);
        //                 String head = rt.getColumnHeading(i);
        //                 if (head!=null && col!=null)
        //                     count++;
        //             }
        //             return count;
        //         }
        //
        //         void multiPlot()
        //         {
        //             ImagePlus imp = getImage();
        //             if (imp==null) return;
        //             int[] indexes = getIndexes();
        //             int n = indexes.length;
        //             if (n==0) return;
        //             if (!imp.lock ()) return;
        //             Color[] colors = { Color.blue, Color.green, Color.magenta, Color.red, Color.cyan, Color.yellow };
        //             if (n>colors.length)
        //             {
        //                 colors = new Color[n];
        //                 double c = 0;
        //                 double inc = 150.0/n;
        //                 for (int i = 0; i<n; i++)
        //                 {
        //                     colors[i] = new Color((int)c, (int)c, (int)c);
        //                     c += inc;
        //                 }
        //             }
        //             int currentSlice = imp.getCurrentSlice();
        //             double[][] x = new double[n][];
        //             double[][] y = new double[n][];
        //             double minY = Double.MAX_VALUE;
        //             double maxY = -Double.MAX_VALUE;
        //             double fixedMin = ProfilePlot.getFixedMin();
        //             double fixedMax = ProfilePlot.getFixedMax();
        //             boolean freeYScale = fixedMin==0.0 && fixedMax==0.0;
        //             if (!freeYScale)
        //             {
        //                 minY = fixedMin;
        //                 maxY = fixedMax;
        //             }
        //             int maxX = 0;
        //             Calibration cal = imp.getCalibration();
        //             double xinc = cal.pixelWidth;
        //             for (int i = 0; i<indexes.length; i++)
        //             {
        //                 if (!restore(getImage(), indexes[i], true)) break;
        //                 Roi roi = imp.getRoi();
        //                 if (roi==null) break;
        //                 if (roi.isArea() && roi.getType()!=Roi.RECTANGLE)
        //                     IJ.run(imp, "Area to Line", "");
        //                 ProfilePlot pp = new ProfilePlot(imp, Prefs.verticalProfile||IJ.altKeyDown());
        //                 y[i] = pp.getProfile();
        //                 if (y[i]==null) break;
        //                 if (y[i].length>maxX) maxX = y[i].length;
        //                 if (freeYScale)
        //                 {
        //                     double[] a = Tools.getMinMax(y[i]);
        //                     if (a[0]<minY) minY=a[0];
        //                     if (a[1]>maxY) maxY = a[1];
        //                 }
        //                 double[] xx = new double[y[i].length];
        //                 for (int j = 0; j<xx.length; j++)
        //                     xx[j] = j*xinc;
        //                 x[i] = xx;
        //             }
        //             String xlabel = "Distance ("+cal.getUnits()+")";
        //             Plot plot = new Plot("Profiles", xlabel, "Value", x[0], y[0]);
        //             plot.setLimits(0, maxX*xinc, minY, maxY);
        //             for (int i = 1; i<indexes.length; i++)
        //             {
        //                 plot.setColor(colors[i]);
        //                 if (x[i]!=null)
        //                     plot.addPoints(x[i], y[i], Plot.LINE);
        //             }
        //             plot.setColor(colors[0]);
        //             if (x[0]!=null)
        //                 plot.show();
        //             imp.setSlice(currentSlice);
        //             if (indexes.length>1)
        //                 IJ.run("Select None");
        //             if (record()) Recorder.record("roiManager", "Multi Plot");
        //             imp.unlock();
        //         }
        //
        //         private void multiCrop()
        //         {
        //             ImagePlus imp = getImage();
        //             if (imp==null)
        //                 return;
        //             int[] indexes = getIndexes();
        //             int n = indexes.length;
        //             String directory = Prefs.get(MULTI_CROP_DIR, IJ.getDir("downloads")+"stack/");
        //             String[] formats = { "tif", "png", "jpg" };
        //             GenericDialog gd = new GenericDialog("Multi Crop");
        //             gd.setInsets(5, 0, 0);
        //             gd.addDirectoryField("Dir:", directory);
        //             gd.setInsets(2, 70, 10);
        //             gd.addMessage("drag and drop target", IJ.font10, Color.darkGray);
        //             gd.addChoice("Format:", formats, formats[multiCropFormatIndex]);
        //             gd.addCheckbox("Show "+n+" cropped images:", multiCropShow);
        //             gd.addCheckbox("Save "+n+" cropped images:", multiCropSave);
        //             gd.showDialog();
        //             if (gd.wasCanceled())
        //                 return;
        //             directory = gd.getNextString();
        //             directory = IJ.addSeparator(directory);
        //             Prefs.set(MULTI_CROP_DIR, directory);
        //             multiCropFormatIndex = gd.getNextChoiceIndex();
        //             String format = formats[multiCropFormatIndex];
        //             multiCropShow = gd.getNextBoolean();
        //             multiCropSave = gd.getNextBoolean();
        //             String options = "";
        //             if (multiCropShow) options += " show";
        //             if (multiCropSave)
        //             {
        //                 options += " save";
        //                 options += " "+format;
        //             }
        //             if (record())
        //             {
        //                 String dir = Recorder.fixPath(directory);
        //                 if (Recorder.scriptMode())
        //                     Recorder.recordCall("rm.multiCrop(\""+dir+"\", \""+options+"\");");
        //                 else
        //                     Recorder.record("RoiManager.multiCrop", dir, options);
        //             }
        //             multiCrop(directory, options);
        //         }
        //
        //         public void multiCrop(String directory, String options)
        //         {
        //             ImagePlus imp = getImage();
        //             if (imp==null)
        //                 return;
        //             Roi roiOrig = imp.getRoi();
        //             Roi[] rois = getSelectedRoisAsArray();
        //             ImagePlus[] images = imp.crop(rois);
        //             if (options==null) options = "";
        //             if (options.contains("show"))
        //             {
        //                 ImageStack stack = ImageStack.create(images);
        //                 ImagePlus imgStack = new ImagePlus("CROPPED_"+getTitle(), stack);
        //                 Overlay overlay = Overlay.createStackOverlay(rois);
        //                 imgStack.setOverlay(overlay);
        //                 imgStack.show();
        //                 if (roiOrig==null)
        //                     imp.deleteRoi();
        //             }
        //             if (options.contains("save"))
        //             {
        //                 String format = "tif";
        //                 if (options.contains("png")) format = "png";
        //                 if (options.contains("jpg")) format = "jpg";
        //                 for (int i = 0; i<images.length; i++)
        //                 {
        //                     Rectangle bounds = rois[i].getBounds();
        //                     String title = IJ.pad(bounds.x, 4)+"-"+IJ.pad(bounds.y, 4);
        //                     String path = directory + title + "." + format;
        //                     IJ.saveAs(images[i], format, path);
        //                 }
        //                 /*
        //                 if (options.contains("show")) {
        //                     int width = 1;
        //                     int height = 1;
        //                     for (int i=0; i<rois.length; i++) {
        //                         Rectangle bounds = rois[i].getBounds();
        //                         if (bounds.width>width) width = bounds.width;
        //                         if (bounds.height>height) height = bounds.height;
        //                     }
        //                     imp = FolderOpener.open(directory, width, height, "virtual");
        //                     if (imp!=null) imp.show();
        //                 }
        //                 */
        //             }
        //         }
        //
        //         /** Sets the group for the selected ROIs. */
        //         public void setGroup(int group)
        //         {
        //             int[] indexes = getIndexes();
        //             for (int i: indexes)
        //             {
        //                 Roi roi = getRoi(i);
        //                 roi.setGroup(group);
        //             }
        //             ImagePlus imp = WindowManager.getCurrentImage();
        //             if (imp!=null) imp.draw();
        //         }
        //
        //         /** Sets the position for the selected ROIs. */
        //         public void setPosition(int position)
        //         {
        //             int[] indexes = getIndexes();
        //             for (int i: indexes)
        //             {
        //                 Roi roi = getRoi(i);
        //                 roi.setPosition(position);
        //             }
        //             ImagePlus imp = WindowManager.getCurrentImage();
        //             if (imp!=null) imp.draw();
        //         }
        //
        //         /** Obsolete; replaced by RoiManager.setGroup() macro function. */
        //         public static void setGroup(String group)
        //         {
        //             RoiManager rm = getInstance();
        //             if (rm==null) return;
        //             int groupInt = (int)Tools.parseDouble(group, 0);
        //             if (groupInt>0)
        //                 rm.setGroup(groupInt);
        //         }
        //
        //         boolean drawOrFill(int mode)
        //         {
        //             int[] indexes = getIndexes();
        //             ImagePlus imp = getImage();
        //             if (imp==null) return false;
        //             if (!imp.lock ()) return false;
        //             imp.deleteRoi();
        //             ImageProcessor ip = imp.getProcessor();
        //             ip.setColor(Toolbar.getForegroundColor());
        //             ip.snapshot();
        //             Undo.setup(Undo.FILTER, imp);
        //             Filler filler = mode==LABEL ? new Filler() : null;
        //             int slice = imp.getCurrentSlice();
        //             for (int i = 0; i<indexes.length; i++)
        //             {
        //                 Roi roi = (Roi)rois.get(indexes[i]);
        //                 int type = roi.getType();
        //                 if (roi==null) continue;
        //                 if (mode==FILL&&(type==Roi.POLYLINE||type==Roi.FREELINE||type==Roi.ANGLE))
        //                     mode = DRAW;
        //                 String name = (String)listModel.getElementAt(indexes[i]);
        //                 int slice2 = getSliceNumber(roi, name);
        //                 if (slice2>=1 && slice2<=imp.getStackSize())
        //                 {
        //                     imp.setSlice(slice2);
        //                     ip = imp.getProcessor();
        //                     ip.setColor(Toolbar.getForegroundColor());
        //                     if (slice2!=slice) Undo.reset();
        //                 }
        //                 switch (mode)
        //                 {
        //                     case DRAW: roi.drawPixels(ip); break;
        //                     case FILL: ip.fill(roi); break;
        //                     case LABEL:
        //                         roi.drawPixels(ip);
        //                         filler.drawLabel(imp, ip, i+1, roi.getBounds());
        //                         break;
        //                 }
        //             }
        //             if (record() && (mode==DRAW||mode==FILL))
        //                 Recorder.record("roiManager", mode==DRAW ? "Draw" : "Fill");
        //             if (showAllCheckbox.getState())
        //                 runCommand("show none");
        //             imp.updateAndDraw();
        //             imp.unlock();
        //             return true;
        //         }
        //
        //         void setProperties(Color color, int lineWidth, Color fillColor)
        //         {
        //             boolean showDialog = color==null && lineWidth==-1 && fillColor==null;
        //             int[] indexes = getIndexes();
        //             int n = indexes.length;
        //             if (n==0) return;
        //             Roi rpRoi = null;
        //             String rpName = null;
        //             Font font = null;
        //             int justification = TextRoi.LEFT;
        //             String roiText = null;
        //             double opacity = -1;
        //             int pointType = -1;
        //             int pointSize = -1;
        //             int group = -1;
        //             int position = -1;
        //             if (showDialog)
        //             {
        //                 //String label = (String) listModel.getElementAt(indexes[0]);
        //                 rpRoi = (Roi)rois.get(indexes[0]);
        //                 if (n==1)
        //                 {
        //                     fillColor =  rpRoi.getFillColor();
        //                     rpName = rpRoi.getName();
        //                 }
        //                 if (rpRoi.getStrokeColor()==null)
        //                     rpRoi.setStrokeColor(Roi.getColor());
        //                 rpRoi = (Roi)rpRoi.clone();
        //                 if (n>1)
        //                     rpRoi.setName("range: "+(indexes[0]+1)+"-"+(indexes[n-1]+1));
        //                 rpRoi.setFillColor(fillColor);
        //                 RoiProperties rp = new RoiProperties("Properties ", rpRoi); // " "=show "List coordinates"
        //                 if (!rp.showDialog())
        //                     return;
        //                 // Recover parameters of the Property window that were stored in the "transient" roi
        //                 lineWidth = (int)rpRoi.getStrokeWidth();
        //                 defaultLineWidth = lineWidth;
        //                 color =  rpRoi.getStrokeColor();
        //                 fillColor =  rpRoi.getFillColor();
        //                 group = rpRoi.getGroup();
        //                 position = rpRoi.getPosition();
        //                 defaultColor = color;
        //                 if (rpRoi instanceof TextRoi) {
        //                     font = ((TextRoi)rpRoi).getCurrentFont();
        //                     justification = ((TextRoi)rpRoi).getJustification();
        //                     roiText = ((TextRoi)rpRoi).getText();
        //                 }
        //                 if (rpRoi instanceof ImageRoi)
        //     opacity = ((ImageRoi)rpRoi).getOpacity();
        //                 if (rpRoi instanceof PointRoi) {
        //                     pointType = ((PointRoi)rpRoi).getPointType();
        //                     pointSize = ((PointRoi)rpRoi).getSize();
        //                 }
        //             }
        //             ImagePlus imp = WindowManager.getCurrentImage();
        //             if (n==getCount() && n>1 && !IJ.isMacro() && imp!=null && imp.getWindow()!=null)
        //             {
        //                 GenericDialog gd = new GenericDialog("ROI Manager");
        //                 gd.addMessage("Apply changes to all "+n+" selections?");
        //                 gd.showDialog();
        //                 if (gd.wasCanceled()) return;
        //             }
        //             for (int i = 0; i<n; i++)
        //             {
        //                 Roi roi = (Roi)rois.get(indexes[i]);
        //                 if (roi==null) continue;
        //                 if (color!=null)
        //                     roi.setStrokeColor(color);
        //                 if (lineWidth>=0)
        //                     roi.setStrokeWidth(lineWidth);
        //                 roi.setFillColor(fillColor);
        //                 if (group>=0)
        //                     roi.setGroup(group);
        //                 if (rpRoi!=null)
        //                 {
        //                     if (rpRoi.hasHyperStackPosition())
        //                         roi.setPosition(rpRoi.getCPosition(), rpRoi.getZPosition(), rpRoi.getTPosition());
        //                     else
        //                         roi.setPosition(rpRoi.getPosition());
        //                 }
        //                 if ((roi instanceof TextRoi) && showDialog) {
        //                 roi.setImage(imp);
        //                 if (font!=null)
        //                     ((TextRoi)roi).setCurrentFont(font);
        //                 ((TextRoi)roi).setJustification(justification);
        //                 if (n==1) ((TextRoi)roi).setText(roiText);
        //                 roi.setImage(null);
        //             }
        //             if ((roi instanceof ImageRoi) && opacity!=-1)
        //     ((ImageRoi)roi).setOpacity(opacity);
        //             if (roi instanceof PointRoi) {
        //                 if (pointType!=-1) ((PointRoi)roi).setPointType(pointType);
        //                 if (pointSize!=-1) ((PointRoi)roi).setSize(pointSize);
        //             }
        //         }
        //   if (rpRoi!=null && rpName!=null && !rpRoi.getName().equals(rpName))
        //    rename(rpRoi.getName());
        //         ImageCanvas ic = imp!=null ? imp.getCanvas() : null;
        //         Roi roi = imp!=null ? imp.getRoi() : null;
        //         boolean showingAll = ic!=null &&  ic.getShowAllROIs();
        //   if (roi!=null && (n==1||!showingAll)) {
        //    if (lineWidth>=0) roi.setStrokeWidth(lineWidth);
        //    if (color!=null) roi.setStrokeColor(color);
        //    if (group>=0) roi.setGroup(group);
        //    if (fillColor!=null) roi.setFillColor(fillColor);
        //    if (roi!=null && (roi instanceof TextRoi)) {
        //
        //                 ((TextRoi) roi).setCurrentFont(font);
        //         ((TextRoi) roi).setJustification(justification);
        //         ((TextRoi) roi).setText(roiText);
        //     }
        //    if (roi!=null && (roi instanceof ImageRoi) && opacity!=-1)
        //     ((ImageRoi) roi).setOpacity(opacity);
        // }
        // if (lineWidth>1 && !showingAll && roi==null)
        // {
        //     showAll(SHOW_ALL);
        //     showingAll = true;
        // }
        // if (imp!=null) imp.draw();
        // if (record())
        // {
        //     if (group>=0)
        //     {
        //         if (Recorder.scriptMode())
        //             Recorder.recordCall("rm.setGroup("+group+");");
        //         else
        //             Recorder.record("RoiManager.setGroup", group);
        //     }
        //     if (position>=0)
        //     {
        //         if (Recorder.scriptMode())
        //             Recorder.recordCall("rm.setPosition("+position+");");
        //         else
        //             Recorder.record("RoiManager.setPosition", position);
        //     }
        //     if (fillColor!=null)
        //         Recorder.record("roiManager", "Set Fill Color", Colors.colorToString(fillColor));
        //     else
        //     {
        //         if (group==0)
        //             Recorder.record("roiManager", "Set Color", Colors.colorToString(color!=null ? color : Color.red));
        //         Recorder.record("roiManager", "Set Line Width", lineWidth);
        //     }
        // }
        //  }
        //
        //  void flatten()
        // {
        //     ImagePlus imp = WindowManager.getCurrentImage();
        //     if (imp==null)
        //     { IJ.noImage(); return; }
        //     ImageCanvas ic = imp.getCanvas();
        //     if ((ic!=null && ic.getShowAllList()==null) && imp.getOverlay()==null && imp.getRoi()==null)
        //         error("Image does not have an overlay or ROI");
        //     else
        //         IJ.doCommand("Flatten"); // run Image>Flatten in separate thread
        // }
        //
        // public boolean getDrawLabels()
        // {
        //     return labelsCheckbox.getState();
        // }
        //
        // private void combine()
        // {
        //     ImagePlus imp = getImage();
        //     if (imp==null)
        //         return;
        //     Roi[] rois = getSelectedRoisAsArray();
        //     if (rois.length==1 && !IJ.isMacro())
        //     {
        //         error("More than one item must be selected, or none");
        //         return;
        //     }
        //     if (countPointRois(rois)==rois.length)
        //         combinePoints(imp, rois);
        //     else
        //         combineRois(imp, rois);
        // }
        //
        // private int countPointRois(Roi[] rois)
        // {
        //     int nPointRois = 0;
        //     for (Roi roi : rois)
        //    if (roi.getType()==Roi.POINT)
        //     nPointRois++;
        // return nPointRois;
        //  }
        //
        //  private void combineRois(ImagePlus imp, Roi[] rois)
        // {
        //     if (rois.length==1)
        //     {
        //         Roi roi2 = (Roi)rois[0].clone();
        //         roi2.setPosition(0);
        //         roi2.setGroup(0);
        //         imp.setRoi(roi2);
        //         return;
        //     }
        //     IJ.resetEscape();
        //     ShapeRoi s1 = null, s2 = null;
        //     for (int i = 0; i<rois.length; i++)
        //     {
        //         IJ.showProgress(i, rois.length-1);
        //         if (IJ.escapePressed())
        //         {
        //             IJ.showProgress(1.0);
        //             return;
        //         }
        //         Roi roi = rois[i];
        //         if (!roi.isArea() && roi.getType()!=Roi.POINT)
        //             roi = Roi.convertLineToArea(roi);
        //         if (s1==null)
        //         {
        //             if (roi instanceof ShapeRoi)
        // 	    s1 = (ShapeRoi)roi.clone();
        //
        //                 else
        //     s1 = new ShapeRoi(roi);
        // if (s1==null) return;
        //    } else
        // {
        //     if (roi instanceof ShapeRoi)
        // 	    s2 = (ShapeRoi)roi;
        //
        //                 else
        //         s2 = new ShapeRoi(roi);
        //     if (s2==null) continue;
        //     s1.or(s2);
        // }
        //   }
        //   if (s1!=null)
        //     imp.setRoi(s1.trySimplify());
        //  }
        //
        //  void combinePoints(ImagePlus imp, Roi[] rois)
        // {
        //     int n = rois.length;
        //     FloatPolygon fp = new FloatPolygon();
        //     for (int r = 0; r<n; r++)
        //     {
        //         FloatPolygon fpi = rois[r].getFloatPolygon();
        //         for (int i = 0; i<fpi.npoints; i++)
        //             fp.addPoint(fpi.xpoints[i], fpi.ypoints[i]);
        //     }
        //     imp.setRoi(new PointRoi(fp));
        // }
        //
        // /** calculates the intersection of area, line and point selections.
        //  *  If there is one PointRoi in the list of selected Rois, the points inside all selected area rois are kept.
        //  *  If more than one PointRoi is selected, the PointRois get converted to area rois with each pixel containing
        //  *  at least one point selected. */
        // void and()
        // {
        //     ImagePlus imp = getImage();
        //     if (imp==null) return;
        //     Roi[] rois = getSelectedRoisAsArray();
        //     if (rois.length==1)
        //     {
        //         error("More than one item must be selected, or none");
        //         return;
        //     }
        //     int nPointRois = countPointRois(rois);
        //     ShapeRoi s1 = null;
        //     PointRoi pointRoi = null;
        //     for (Roi roi : rois) {
        //     if (roi==null)
        //         continue;
        //     if (s1==null)
        //     {
        //         if (nPointRois==1 && roi.getType() == Roi.POINT)
        //         {
        //             pointRoi = (PointRoi)roi;
        //             continue;  //PointRoi will be handled at the end
        //         }
        //         if (roi instanceof ShapeRoi)
        // 	    s1 = (ShapeRoi)roi.clone();
        //
        //                 else
        //             s1 = new ShapeRoi(roi);
        //         if (s1==null) continue;
        //     }
        //     else
        //     {
        //         if (nPointRois==1 && roi.getType()==Roi.POINT)
        //         {
        //             pointRoi = (PointRoi)roi;
        //             continue;  //PointRoi will be handled at the end
        //         }
        //         ShapeRoi s2 = null;
        //         if (roi instanceof ShapeRoi)
        // 	    s2 = (ShapeRoi)roi.clone();
        //
        //                 else
        //             s2 = new ShapeRoi(roi);
        //         if (s2==null) continue;
        //         s1.and(s2);
        //     }
        // }
        // if (s1==null) return;
        // if (pointRoi!=null)
        //     imp.setRoi(pointRoi.containedPoints(s1));
        // else
        //     imp.setRoi(s1.trySimplify());
        // if (record())
        //     Recorder.record("roiManager", "AND");
        //  }
        //
        //  void xor()
        // {
        //     ImagePlus imp = getImage();
        //     if (imp==null) return;
        //     Roi[] rois = getSelectedRoisAsArray();
        //     if (rois.length==1)
        //     {
        //         error("More than one item must be selected, or none");
        //         return;
        //     }
        //     Roi roi2 = Roi.xor(getSelectedRoisAsArray());
        //     if (roi2!=null) imp.setRoi(roi2);
        //     if (record()) Recorder.record("roiManager", "XOR");
        // }
        //
        // void addParticles()
        // {
        //     String err = IJ.runMacroFile("ij.jar:AddParticles", null);
        //     if (err!=null && err.length()>0)
        //         error(err);
        // }
        //
        // void sort()
        // {
        //     int n = listModel.size();
        //     if (n==0)
        //         return;
        //     String[] labels = new String[n];
        //     for (int i = 0; i<n; i++)
        //         labels[i] = (String)listModel.get(i);
        //     int[] indices = Tools.rank(labels);
        //     Roi[] rois2 = getRoisAsArray();
        //     listModel.removeAllElements();
        //     rois.clear();
        //     for (int i = 0; i<labels.length; i++)
        //     {
        //         listModel.addElement(labels[indices[i]]);
        //         rois.add(rois2[indices[i]]);
        //     }
        //     if (record()) Recorder.record("roiManager", "Sort");
        // }
        //
        // void specify()
        // {
        //     try { IJ.run("Specify..."); }
        //     catch (Exception e) { return; }
        //     runCommand("add");
        // }
        //
        // private static boolean channel = false, slice = true, frame = false;
        //
        // private void removePositions(int position)
        // {
        //     int[] indexes = getIndexes();
        //     if (indexes.length==0)
        //         return;
        //     boolean removeChannels = position==CHANNEL;
        //     boolean removeFrames = position==FRAME;
        //     boolean removeSlices = !(removeChannels||removeFrames);
        //     ImagePlus imp = WindowManager.getCurrentImage();
        //     if (position==SHOW_DIALOG)
        //     {
        //         if (imp!=null && !imp.isHyperStack())
        //         {
        //             channel=false; slice=true; frame=false;
        //         }
        //         if (imp!=null && imp.isHyperStack())
        //         {
        //             channel = slice = frame = false;
        //             if (imp.getNSlices()>1)
        //                 slice = true;
        //             if (imp.getNFrames()>1 && imp.getNSlices()==1)
        //                 frame = true;
        //         }
        //         Font font = new Font("SansSerif", Font.BOLD, 12);
        //         GenericDialog gd = new GenericDialog("Remove");
        //         gd.setInsets(5, 15, 0);
        //         gd.addMessage("Remove positions for:      ", font);
        //         gd.setInsets(6, 25, 0);
        //         gd.addCheckbox("Channels:", channel);
        //         gd.setInsets(0, 25, 0);
        //         gd.addCheckbox("Slices:", slice);
        //         gd.setInsets(0, 25, 0);
        //         gd.addCheckbox("Frames:", frame);
        //         gd.showDialog();
        //         if (gd.wasCanceled())
        //             return;
        //         removeChannels = gd.getNextBoolean();
        //         removeSlices = gd.getNextBoolean();
        //         removeFrames = gd.getNextBoolean();
        //     }
        //     if (!removeChannels && !removeSlices && !removeFrames)
        //     {
        //         slice = true;
        //         return;
        //     }
        //     for (int i = 0; i<indexes.length; i++)
        //     {
        //         int index = indexes[i];
        //         Roi roi = (Roi)rois.get(index);
        //         String name = (String)listModel.getElementAt(index);
        //         int n = getSliceNumber(name);
        //         if (n>0)
        //         {
        //             String name2 = name.substring(5, name.length());
        //             roi.setName(name2);
        //             rois.set(index, roi);
        //             listModel.setElementAt(name2, index);
        //         }
        //         int c = roi.getCPosition();
        //         int z = roi.getZPosition();
        //         int t = roi.getTPosition();
        //         if (c>0 || t>0)
        //         {
        //             if (removeChannels) c = 0;
        //             if (removeSlices) z = 0;
        //             if (removeFrames) t = 0;
        //             roi.setPosition(c, z, t);
        //         }
        //         else
        //             roi.setPosition(0);
        //     }
        //     if (imp!=null)
        //         imp.draw();
        //     if (record())
        //     {
        //         if (removeChannels) Recorder.record("roiManager", "Remove Channel Info");
        //         if (removeSlices) Recorder.record("roiManager", "Remove Slice Info");
        //         if (removeFrames) Recorder.record("roiManager", "Remove Frame Info");
        //     }
        // }
        //
        // private void help()
        // {
        //     String macro = "run('URL...', 'url="+IJ.URL+"/docs/menus/analyze.html#manager');";
        //     new MacroRunner(macro);
        // }
        //
        // private void labels()
        // {
        //     ImagePlus imp = WindowManager.getCurrentImage();
        //     if (imp!=null)
        //     {
        //         showAllCheckbox.setState(true);
        //         labelsCheckbox.setState(true);
        //         showAll(LABELS);
        //     }
        //     try
        //     {
        //         IJ.run("Labels...");
        //     }
        //     catch (Exception e) { }
        //     Overlay defaultOverlay = OverlayLabels.createOverlay();
        //     Prefs.useNamesAsLabels = defaultOverlay.getDrawNames();
        // }
        //
        // private void options()
        // {
        //     Color c = ImageCanvas.getShowAllColor();
        //     GenericDialog gd = new GenericDialog("Options");
        //     //gd.addPanel(makeButtonPanel(gd), GridBagConstraints.CENTER, new Insets(5, 0, 0, 0));
        //     gd.addCheckbox("Associate \"Show All\" ROIs with slices", Prefs.showAllSliceOnly);
        //     gd.addCheckbox("Restore ROIs centered", restoreCentered);
        //     gd.addCheckbox("Use ROI names as labels", Prefs.useNamesAsLabels);
        //     gd.showDialog();
        //     if (gd.wasCanceled())
        //     {
        //         if (c!=ImageCanvas.getShowAllColor())
        //             ImageCanvas.setShowAllColor(c);
        //         return;
        //     }
        //     Prefs.showAllSliceOnly = gd.getNextBoolean();
        //     restoreCentered = gd.getNextBoolean();
        //     Prefs.useNamesAsLabels = gd.getNextBoolean();
        //     ImagePlus imp = WindowManager.getCurrentImage();
        //     if (imp!=null)
        //     {
        //         Overlay overlay = imp.getOverlay();
        //         if (overlay==null)
        //         {
        //             ImageCanvas ic = imp.getCanvas();
        //             if (ic!=null)
        //                 overlay = ic.getShowAllList();
        //         }
        //         if (overlay!=null)
        //         {
        //             overlay.drawNames(Prefs.useNamesAsLabels);
        //             setOverlay(imp, overlay);
        //         }
        //         else
        //             imp.draw();
        //     }
        //     if (record())
        //     {
        //         Recorder.record("roiManager", "Associate", Prefs.showAllSliceOnly ? "true" : "false");
        //         Recorder.record("roiManager", "Centered", restoreCentered ? "true" : "false");
        //         Recorder.record("roiManager", "UseNames", Prefs.useNamesAsLabels ? "true" : "false");
        //     }
        // }
        //
        // Panel makeButtonPanel(GenericDialog gd)
        // {
        //     Panel panel = new Panel();
        //     //buttons.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 0));
        //     colorButton = new Button("\"Show All\" Color...");
        //     colorButton.addActionListener(this);
        //     panel.add(colorButton);
        //     return panel;
        // }
        //
        // void setShowAllColor()
        // {
        //     ColorChooser cc = new ColorChooser("\"Show All\" Color", ImageCanvas.getShowAllColor(), false);
        //     ImageCanvas.setShowAllColor(cc.getColor());
        // }
        //
        // void split()
        // {
        //     ImagePlus imp = getImage();
        //     if (imp==null) return;
        //     Roi roi = imp.getRoi();
        //     if (roi==null || roi.getType()!=Roi.COMPOSITE)
        //     {
        //         error("Image with composite selection required");
        //         return;
        //     }
        //     boolean record = Recorder.record;
        //     Recorder.record = false;
        //     Roi[] rois = ((ShapeRoi)roi).getRois();
        //     for (int i = 0; i<rois.length; i++)
        //     {
        //         imp.setRoi(rois[i]);
        //         addRoi(false);
        //     }
        //     Recorder.record = record;
        //     if (record()) Recorder.record("roiManager", "Split");
        // }
        //
        // void showAll(int mode)
        // {
        //     ImagePlus imp = WindowManager.getCurrentImage();
        //     if (imp==null)
        //         return;
        //     boolean showAll = mode==SHOW_ALL;
        //     if (showAll)
        //         imageID = imp.getID();
        //     if (mode==LABELS || mode==NO_LABELS)
        //         showAll = true;
        //     if (showAll)
        //         imp.deleteRoi();
        //     if (mode==SHOW_NONE)
        //     {
        //         removeOverlay(imp);
        //         imageID = 0;
        //     }
        //     else if (getCount()>0)
        //     {
        //         Roi[] rois = getRoisAsArray();
        //         Overlay overlay = newOverlay();
        //         for (int i = 0; i<rois.length; i++)
        //             overlay.add(rois[i]);
        //         setOverlay(imp, overlay);
        //     }
        // }
        //
        void updateShowAll()
        {
            ImagePlus imp = WindowManager.getCurrentImage();
            if (imp==null)
                return;
            //todo:
            // if (showAllCheckbox.Checked)
            // {
            //     if (getCount()>0)
            //     {
            //         Roi[] rois = getRoisAsArray();
            //         Overlay overlay = newOverlay();
            //         for (int i = 0; i<rois.Length; i++)
            //             overlay.add(rois[i]);
            //         setOverlay(imp, overlay);
            //     }
            //     else
            //         removeOverlay(imp);
            // }
            // else
            //     removeOverlay(imp);
        }
        
        int[] getAllIndexes()
        {
            int count = getCount();
            int[] indexes = new int[count];
            for (int i = 0; i<count; i++)
                indexes[i] = i;
            return indexes;
        }
        
        ImagePlus getImage()
        {
            ImagePlus imp = WindowManager.getCurrentImage();
            if (imp==null)
            {
                error("There are no images open.");
                return null;
            }
            else
                return imp;
        }
        
        bool error(string msg)
        {
            // new MessageDialog(this, "ROI Manager", msg);
            MessageBox.Show(msg, "ROI Manager", MessageBoxButtons.OK);
            Macro.abort();
            return false;
        }
        //
        // public void processWindowEvent(WindowEvent e)
        // {
        //     super.processWindowEvent(e);
        //     if (e.getID()==WindowEvent.WINDOW_CLOSING)
        //     {
        //         instance = null;
        //     }
        //     if (!IJ.isMacro())
        //         ignoreInterrupts = false;
        // }
        //
        // /** Returns a reference to the ROI Manager if it
        //  * open or opens it if it is not already open.
        // */
        // public static RoiManager getRoiManager()
        // {
        //     if (instance!=null)
        //         return (RoiManager)instance;
        //     else
        //         return new RoiManager();
        // }
        //
        // /** Returns a reference to the ROI Manager, or null if it is not open
        //  * and a batch mode macro is not running. If the ROI Manager
        //  * is not open and a batch mode macro is running,
        //  * returns the hidden batch mode RoiManager.
        //  * @see #getRoiManager
        // */
        // public static RoiManager getInstance()
        // {
        //     if (instance==null && IJ.isMacro())
        //         return Interpreter.getBatchModeRoiManager();
        //     else
        //         return (RoiManager)instance;
        // }
        //
        // public static RoiManager getRawInstance()
        // {
        //     return (RoiManager)instance;
        // }
        //
        // /** Returns a reference to the ROI Manager window or to the
        //     macro batch mode RoiManager, or null if neither exists. */
        // public static RoiManager getInstance2()
        // {
        //     RoiManager rm = getInstance();
        //     if (rm==null && IJ.isMacro())
        //         rm = Interpreter.getBatchModeRoiManager();
        //     return rm;
        // }
        //
        // /** Obsolete
        //  * @deprecated
        //  * @see #getCount
        //  * @see #getRoisAsArray
        // */
        // public Hashtable getROIs()
        // {
        //     Roi[] rois = getRoisAsArray();
        //     Hashtable ht = new Hashtable();
        //     for (int i = 0; i<rois.length; i++)
        //         ht.put((String)listModel.getElementAt(i), rois[i]);
        //     return ht;
        // }
        //
        // /** Obsolete
        //  * @deprecated
        //  * @see #getCount
        //  * @see #getRoisAsArray
        //  * @see #getSelectedIndex
        // */
        // public List getList()
        // {
        //     List awtList = new List();
        //     for (int i = 0; i<getCount(); i++)
        //         awtList.add((String)listModel.getElementAt(i));
        //     int index = getSelectedIndex();
        //     if (index>=0)
        //         awtList.select(index);
        //     return awtList;
        // }
        //
        /** Returns the ROI count. */
        public int getCount()
        {
            return list.Items.Count;
        }
        //
        // /** Returns the count of selected ROIs. */
        // public int selected()
        // {
        //     return getSelectedIndexes().length;
        // }
        //
        // /** Returns the index of the specified Roi, or -1 if it is not found. */
        // public int getRoiIndex(Roi roi)
        // {
        //     int n = getCount();
        //     for (int i = 0; i<n; i++)
        //     {
        //         Roi roi2 = (Roi)rois.get(i);
        //         if (roi==roi2)
        //             return i;
        //     }
        //     return -1;
        // }
        //
        // /** Returns the index of the first selected ROI or -1 if no ROI is selected. */
        // public int getSelectedIndex()
        // {
        //     return list.getSelectedIndex();
        // }
        //
        // /** Returns a reference to the ROI at the specified index. */
        // public Roi getRoi(int index)
        // {
        //     if (index<0 || index>=getCount())
        //         return null;
        //     return (Roi)rois.get(index);
        // }
        //
        // /** Returns the ROIs as an array. */
        // public synchronized Roi[] getRoisAsArray() {
        //     Roi[] array = new Roi[rois.size()];
        //     return (Roi[])rois.toArray(array);
        // }
        //
        // /** Returns the selected ROIs as an array, or
        //     all the ROIs if none are selected. */
        // public Roi[] getSelectedRoisAsArray()
        // {
        //     int[] indexes = getIndexes();
        //     int n = indexes.length;
        //     Roi[] array = new Roi[n];
        //     for (int i = 0; i<n; i++)
        //         array[i] = (Roi)rois.get(indexes[i]);
        //     return array;
        // }
        //
        // /** Returns the name of the ROI with the specified index,
        //  * or null if the index is out of range.
        //  * See also: RoiManager.getName() macro function.
        // */
        // public String getName(int index)
        // {
        //     if (index>=0 && index<getCount())
        //         return (String)listModel.getElementAt(index);
        //     else
        //         return null;
        // }
        //
        // /** Returns the index of the first ROI with the
        //  * specified name, or -1 if no ROI has that name.
        //  * See also: RoiManager.getIndex() macro function.
        // */
        // public int getIndex(String name)
        // {
        //     Roi[] rois = getRoisAsArray();
        //     for (int i = 0; i<rois.length; i++)
        //     {
        //         if (name.equals(rois[i].getName()))
        //             return i;
        //     }
        //     return -1;
        // }
        //
        // /** Returns the name of the ROI with the specified index.
        //     Can be called from a macro using
        //     <pre>call("ij.plugin.frame.RoiManager.getName", index)</pre>
        //     Returns "null" if the Roi Manager is not open or index is
        //     out of range.
        // */
        // public static String getName(String index)
        // {
        //     int i = (int)Tools.parseDouble(index, -1);
        //     RoiManager instance = getInstance2();
        //     if (instance!=null && i>=0 && i<instance.getCount())
        //         return (String)instance.listModel.getElementAt(i);
        //     else
        //         return "null";
        // }
        //

        /** Executes the ROI Manager "Add", "Add & Draw", "Update", "Delete", "Measure", "Draw",
        "Show All", "Show None", "Fill", "Deselect", "Select All", "Combine", "AND", "XOR", "Split",
        "Sort" or "Multi Measure" command.	Returns false if <code>cmd</code>
        is not one of these strings. */
        public bool runCommand(string cmd)
        {
            cmd = cmd.ToLower();
            macro = true;
            bool ok = true;
            if (cmd.Equals("add"))
            {
                bool shift = IJ.shiftKeyDown();
                bool alt = IJ.altKeyDown();
                if (Interpreter.isBatchMode())
                {
                    shift = false;
                    alt = false;
                }
                add(shift, alt);
                // if (IJ.isJava18()&&IJ.isMacOSX())
                //     repaint();
            }
            // else if (cmd.equals("add & draw"))
            //     addAndDraw(false);
            // else if (cmd.equals("update"))
            //     update(true);
            // else if (cmd.equals("update2"))
            //     update(false);
            // else if (cmd.equals("delete"))
            //     delete(false);
            // else if (cmd.equals("measure"))
            // {
            //     if (EventQueue.isDispatchThread())
            //         measure(getImage());
            //     else try
            //         {
            //             // run on event dispatching thread for greater speed on Windows
            //             final ImagePlus imp = getImage();
            //             EventQueue.invokeAndWait(new Runnable()
            //             {
            //
            //         public void run()
            //             {
            //                 measure(imp);
            //             }
            //         });
            //     } catch (Exception e) { }
            // } 
            //       else if (cmd.equals("draw"))
            //  drawOrFill(DRAW);
            // else if (cmd.equals("fill"))
            //  drawOrFill(FILL);
            // else if (cmd.equals("label"))
            //  drawOrFill(LABEL);
            // else if (cmd.equals("and"))
            //  and();
            // else if (cmd.equals("or") || cmd.equals("combine"))
            //  combine();
            // else if (cmd.equals("xor"))
            //  xor();
            // else if (cmd.equals("split"))
            //  split();
            // else if (cmd.equals("sort"))
            //  sort();
            // else if (cmd.startsWith("multi measure") || cmd.startsWith("multi-measure"))
            //  multiMeasure(cmd);
            // else if (cmd.equals("multi plot"))
            //  multiPlot();
            // else if (cmd.equals("show all")) {
            //  if (WindowManager.getCurrentImage()!=null) {
            //   showAll(SHOW_ALL);
            //               showAllCheckbox.setState(true);
            //  }
            //       } 
            //       else if (cmd.equals("show none"))
            //       {
            //           if (WindowManager.getCurrentImage()!=null)
            //           {
            //               showAll(SHOW_NONE);
            //               showAllCheckbox.setState(false);
            //           }
            //       }
            //       else if (cmd.equals("show all with labels"))
            //       {
            //           labelsCheckbox.setState(true);
            //           showAll(LABELS);
            //           showAllCheckbox.setState(true);
            //           if (Interpreter.isBatchMode()) IJ.wait(250);
            //       }
            //       else if (cmd.equals("show all without labels"))
            //       {
            //           showAllCheckbox.setState(true);
            //           labelsCheckbox.setState(false);
            //           showAll(NO_LABELS);
            //           if (Interpreter.isBatchMode()) IJ.wait(250);
            //       }
            //       else if (cmd.equals("deselect")||cmd.indexOf("all")!=-1)
            //       {
            //           if (IJ.isMacOSX()) ignoreInterrupts = true;
            //           deselect();
            //           IJ.wait(50);
            //       }
            //       else if (cmd.equals("reset"))
            //       {
            //           reset();
            //       }
            //       else if (cmd.equals("debug"))
            //       {
            //           //IJ.log("Debug: "+debugCount);
            //           //for (int i=0; i<debugCount; i++)
            //           //	IJ.log(debug[i]);
            //       }
            //       else if (cmd.equals("enable interrupts"))
            //       {
            //           ignoreInterrupts = false;
            //       }
            //       else if (cmd.equals("remove channel info"))
            //       {
            //           removePositions(CHANNEL);
            //       }
            //       else if (cmd.equals("remove slice info"))
            //       {
            //           removePositions(SLICE);
            //       }
            //       else if (cmd.equals("remove frame info"))
            //       {
            //           removePositions(FRAME);
            //       }
            //       else if (cmd.equals("list"))
            //       {
            //           listRois();
            //       }
            //       else if (cmd.equals("interpolate rois"))
            //       {
            //           interpolateRois();
            //       }
            else
                ok = false;
            macro = false;
            return ok;
        }

        //
        //  /** Using the specified image, runs the ROI Manager "Add", "Add & Draw", "Update",
        //   "Delete", "Measure", "Draw", "Show All", "Show None", "Fill", "Deselect", "Select All",
        //   "Combine", "AND", "XOR", "Split", "Sort" or "Multi Measure" command. */
        //  public boolean runCommand(ImagePlus imp, String cmd)
        // {
        //     WindowManager.setTempCurrentImage(imp);
        //     boolean ok = runCommand(cmd);
        //     WindowManager.setTempCurrentImage(null);
        //     return ok;
        // }
        //
        // /** Executes the ROI Manager "Open", "Save" or "Rename" command. Returns false if
        // <code>cmd</code> is not "Open", "Save" or "Rename", or if an error occurs. */
        // public boolean runCommand(String cmd, String name)
        // {
        //     cmd = cmd.toLowerCase();
        //     macro = true;
        //     if (cmd.equals("open"))
        //     {
        //         boolean ok = open(Opener.makeFullPath(name));
        //         macro = false;
        //         return ok;
        //     }
        //     else if (cmd.equals("save"))
        //     {
        //         boolean ok = false;
        //         if (name!=null && name.endsWith(".roi"))
        //             ok = saveOne(getIndexes(), name);
        //         else
        //             ok = save(name, false);
        //         return ok;
        //     }
        //     else if (cmd.equals("save selected"))
        //     {
        //         if (name!=null && name.endsWith(".roi"))
        //             return saveOne(getIndexes(), name);
        //         else
        //             return save(name, true);
        //     }
        //     else if (cmd.equals("rename"))
        //     {
        //         rename(name);
        //         macro = false;
        //         return true;
        //     }
        //     else if (cmd.equals("set color"))
        //     {
        //         Color color = Colors.decode(name, Color.cyan);
        //         setProperties(color, -1, null);
        //         macro = false;
        //         return true;
        //     }
        //     else if (cmd.equals("set fill color"))
        //     {
        //         Color fillColor = Colors.decode(name, Color.cyan);
        //         setProperties(null, -1, fillColor);
        //         macro = false;
        //         return true;
        //     }
        //     else if (cmd.equals("set line width"))
        //     {
        //         int lineWidth = (int)Tools.parseDouble(name, 0);
        //         if (lineWidth>=0)
        //             setProperties(null, lineWidth, null);
        //         macro = false;
        //         return true;
        //     }
        //     else if (cmd.equals("associate"))
        //     {
        //         Prefs.showAllSliceOnly = name.equals("true") ? true : false;
        //         macro = false;
        //         return true;
        //     }
        //     else if (cmd.equals("centered"))
        //     {
        //         restoreCentered = name.equals("true") ? true : false;
        //         macro = false;
        //         return true;
        //     }
        //     else if (cmd.equals("usenames"))
        //     {
        //         Prefs.useNamesAsLabels = name.equals("true") ? true : false;
        //         macro = false;
        //         if (labelsCheckbox.getState())
        //         {
        //             ImagePlus imp = WindowManager.getCurrentImage();
        //             if (imp!=null) imp.draw();
        //         }
        //         return true;
        //     }
        //     return false;
        // }
        //
        // /** Clears this RoiManager so that it contains no ROIs. */
        // public void reset()
        // {
        //     if (IJ.isMacOSX() && IJ.isMacro())
        //         ignoreInterrupts = true;
        //     if (listModel!=null)
        //         listModel.removeAllElements();
        //     overlayTemplate = null;
        //     rois.clear();
        //     updateShowAll();
        // }
        //
        // private void scale()
        // {
        //     GenericDialog gd = new GenericDialog("Scale");
        //     gd.addNumericField("X scale factor:", xscale, 2, 4, "");
        //     gd.addNumericField("Y scale factor:", yscale, 2, 4, "");
        //     gd.addCheckbox("Centered", scaleCentered);
        //     gd.showDialog();
        //     if (gd.wasCanceled())
        //         return;
        //     xscale = gd.getNextNumber();
        //     yscale = gd.getNextNumber();
        //     scaleCentered = gd.getNextBoolean();
        //     scale(xscale, yscale, scaleCentered);
        //     if (record())
        //     {
        //         if (Recorder.scriptMode())
        //             Recorder.recordCall("rm.scale("+xscale+", "+yscale+", "+scaleCentered+");");
        //         else
        //             Recorder.recordString("RoiManager.scale("+xscale+", "+yscale+", "+scaleCentered+");\n");
        //     }
        // }
        //
        // public void scale(double xscale, double yscale, boolean centered)
        // {
        //     int[] indexes = getIndexes();
        //     for (int i = 0; i<indexes.length; i++)
        //     {
        //         Roi roi = (Roi)rois.get(indexes[i]);
        //         Roi roi2 = RoiScaler.scale(roi, xscale, yscale, centered);
        //         rois.set(indexes[i], roi2);
        //     }
        //     updateShowAll();
        // }
        //
        // private void rotate()
        // {
        //     double xcenter = Double.NaN;
        //     double ycenter = Double.NaN;
        //     GenericDialog gd = new GenericDialog("Rotate");
        //     gd.addNumericField("Angle:", angle, 2, 5, "degrees");
        //     gd.addCheckbox("Rotate around image center", false);
        //     gd.showDialog();
        //     if (gd.wasCanceled())
        //         return;
        //     angle = gd.getNextNumber();
        //     if (angle==0.0)
        //         return;
        //     boolean rotateAroundImageCenter = gd.getNextBoolean();
        //     ImagePlus imp = WindowManager.getCurrentImage();
        //     if (imp!=null && rotateAroundImageCenter)
        //     {
        //         xcenter = imp.getWidth()/2.0;
        //         ycenter = imp.getHeight()/2.0;
        //     }
        //     rotate(angle, xcenter, ycenter);
        //     if (record())
        //     {
        //         if (Recorder.scriptMode())
        //         {
        //             if (Double.isNaN(xcenter))
        //                 Recorder.recordCall("rm.rotate("+angle+");");
        //             else
        //                 Recorder.recordCall("rm.rotate("+angle+", "+xcenter+", "+ycenter+");");
        //         }
        //         else
        //         {
        //             if (Double.isNaN(xcenter))
        //                 Recorder.recordString("RoiManager.rotate("+angle+");\n");
        //             else
        //                 Recorder.recordString("RoiManager.rotate("+angle+", "+xcenter+", "+ycenter+");\n");
        //         }
        //     }
        // }
        //
        // public void rotate(double angle)
        // {
        //     rotate(angle, Double.NaN, Double.NaN);
        // }
        //
        // public void rotate(double angle, double xcenter, double ycenter)
        // {
        //     boolean useRoiCenter = Double.isNaN(xcenter);
        //     int[] indexes = getIndexes();
        //     for (int i = 0; i<indexes.length; i++)
        //     {
        //         Roi roi = (Roi)rois.get(indexes[i]);
        //         if (useRoiCenter)
        //         {
        //             FloatPolygon center = roi.getRotationCenter();
        //             xcenter = center.xpoints[0];
        //             ycenter = center.ypoints[0];
        //         }
        //         Roi roi2 = RoiRotator.rotate(roi, angle, xcenter, ycenter);
        //         rois.set(indexes[i], roi2);
        //     }
        //     updateShowAll();
        // }
        //
        // private void translate()
        // {
        //     GenericDialog gd = new GenericDialog("Translate");
        //     gd.addNumericField("X offset (pixels): ", translateX, 0);
        //     gd.addNumericField("Y offset (pixels): ", translateY, 0);
        //     gd.showDialog();
        //     if (gd.wasCanceled())
        //         return;
        //     translateX = gd.getNextNumber();
        //     translateY = gd.getNextNumber();
        //     translate(translateX, translateY);
        //     if (record())
        //     {
        //         if (Recorder.scriptMode())
        //             Recorder.recordCall("rm.translate("+translateX+", "+translateY+");");
        //         else
        //             Recorder.record("RoiManager.translate", (int)translateX, (int)translateY);
        //     }
        // }
        //
        // /** Moves the selected ROIs or all the ROIs if none are selected. */
        // public void translate(double dx, double dy)
        // {
        //     Roi[] rois = getSelectedRoisAsArray();
        //     for (int i = 0; i<rois.length; i++)
        //         rois[i].translate(dx, dy);
        //     ImagePlus imp = WindowManager.getCurrentImage();
        //     if (imp!=null)
        //         imp.draw();
        // }
        //
        // private boolean save(String name, boolean saveSelected)
        // {
        //     errorMessage = null;
        //     if (!name.endsWith(".zip") && !name.equals(""))
        //     {
        //         errorMessage = "Name must end with '.zip'";
        //         return error(errorMessage);
        //     }
        //     if (getCount()==0)
        //         return error("The list is empty");
        //     int[] indexes = null;
        //     if (saveSelected)
        //         indexes = getIndexes();
        //     else
        //         indexes = getAllIndexes();
        //     boolean ok = false;
        //     if (name.equals(""))
        //         ok = saveMultiple(indexes, null);
        //     else
        //         ok = saveMultiple(indexes, name);
        //     macro = false;
        //     return ok;
        // }
        //
        // /** Adds the current selection to the ROI Manager, using the
        //     specified color (a 6 digit hex string) and line width. */
        // public boolean runCommand(String cmd, String hexColor, double lineWidth)
        // {
        //     if (hexColor==null && lineWidth==1.0 && (IJ.altKeyDown()&&!Interpreter.isBatchMode()))
        //         addRoi(true);
        //     else
        //     {
        //         Color color = hexColor!=null ? Colors.decode(hexColor, Color.cyan) : null;
        //         addRoi(null, false, color, (int)Math.round(lineWidth));
        //     }
        //     return true;
        // }
        //
        // /** Assigns the ROI at the specified index to the current image. */
        // public void select(int index)
        // {
        //     select(null, index);
        // }
        //
        // /** Assigns the ROI at the specified index to 'imp'. */
        // public void select(ImagePlus imp, int index)
        // {
        //     if (index<0)
        //     {
        //         deselect();
        //         return;
        //     }
        //     int n = getCount();
        //     if (index>=n) return;
        //     boolean mm = list.getSelectionMode() == ListSelectionModel.MULTIPLE_INTERVAL_SELECTION;
        //     if (mm)
        //         list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        //     int delay = 1;
        //     long start = System.currentTimeMillis();
        //     while (true)
        //     {
        //         if (list.isSelectedIndex(index))
        //             break;
        //         list.clearSelection();
        //         list.setSelectedIndex(index);
        //     }
        //     if (imp==null)
        //         imp = WindowManager.getCurrentImage();
        //     if (imp!=null)
        //         restore(imp, index, true);
        //     if (mm)
        //         list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        // }
        //
        // public void selectAndMakeVisible(ImagePlus imp, int index)
        // {
        //     select(imp, index);
        //     list.ensureIndexIsVisible(index);
        // }
        //
        // public void select(int index, boolean shiftKeyDown, boolean altKeyDown)
        // {
        //     if (!(shiftKeyDown||altKeyDown))
        //         select(index);
        //     ImagePlus imp = IJ.getImage();
        //     if (imp==null)
        //         return;
        //     Roi previousRoi = imp.getRoi();
        //     if (previousRoi==null)
        //     {
        //         select(index);
        //         return;
        //     }
        //     Roi.setPreviousRoi(previousRoi);
        //     Roi roi = (Roi)rois.get(index);
        //     if (roi!=null)
        //     {
        //         roi.setImage(imp);
        //         roi.update(shiftKeyDown, altKeyDown);
        //     }
        // }
        //
        // /** Selects all ROIs of a given group. */
        // public void selectGroup(int group)
        // {
        //     ArrayList<Integer> listSelected = new ArrayList<Integer>();
        //     for (int i = 0; i<getCount(); i++)
        //     {
        //         Roi roi = getRoi(i);
        //         if (roi.getGroup()==group)
        //             listSelected.add(i);
        //     }
        //     int[] selected = new int[listSelected.size()];
        //     for (int j = 0; j<listSelected.size(); j++)
        //         selected[j] = listSelected.get(j);
        //     setSelectedIndexes(selected);
        // }
        //
        // /** Obsolete; replaced by RoiManager.selectGroup() macro function. */
        // public static void selectGroup(String group)
        // {
        //     RoiManager rm = getInstance();
        //     if (rm==null) return;
        //     int groupInt = (int)Tools.parseDouble(group, 0);
        //     if (groupInt>0) rm.selectGroup(groupInt);
        // }
        //
        public void deselect()
        {
            int n = getCount();
            for (int i = 0; i < n; i++)
                list.SelectedIndex = -1;
            if (record()) Recorder.record("roiManager", "Deselect");
            return;
        }
        
        /** Deselect the specified ROI if it is the only one selected. */
        public void deselect(Roi roi)
        {
            int[] indexes = getSelectedIndexes();
            if (indexes.Length==1 && listModel.Count>0)
            {
                string label = (string)listModel[indexes[0]];
                if (label.Equals(roi.getName()))
                {
                    deselect();
                    Invalidate();
                }
            }
        }
        //
        // public void setEditMode(ImagePlus imp, boolean editMode)
        // {
        //     showAllCheckbox.setState(editMode);
        //     labelsCheckbox.setState(editMode);
        //     showAll(editMode ? LABELS : SHOW_NONE);
        // }
        //
        // /** Overrides PlugInFrame.close(). */
        // public void close()
        // {
        //     super.close();
        //     instance = null;
        //     resetMultiMeasureResults();
        //     Prefs.saveLocation(LOC_KEY, getLocation());
        //     if (!showAllCheckbox.getState() || IJ.macroRunning())
        //         return;
        //     int n = getCount();
        //     ImagePlus imp = WindowManager.getCurrentImage();
        //     if (imp==null || (imp.getCanvas()!=null && imp.getCanvas().getShowAllList()==null))
        //         return;
        //     if (n>0)
        //     {
        //         GenericDialog gd = new GenericDialog("ROI Manager");
        //         gd.addMessage("Save the "+n+" displayed ROIs as an overlay?");
        //         gd.setOKLabel("Discard");
        //         gd.setCancelLabel("Save as Overlay");
        //         gd.showDialog();
        //         if (gd.wasCanceled())
        //             moveRoisToOverlay(imp);
        //         else
        //             removeOverlay(imp);
        //     }
        //     else
        //         imp.draw();
        // }
        //
        // /** Moves all the ROIs to the specified image's overlay. */
        // public void moveRoisToOverlay(ImagePlus imp)
        // {
        //     if (imp==null)
        //         return;
        //     Roi[] rois = getRoisAsArray();
        //     int n = rois.length;
        //     Overlay overlay = imp.getOverlay();
        //     if (overlay==null)
        //         overlay = newOverlay();
        //     for (int i = 0; i<n; i++)
        //     {
        //         Roi roi = (Roi)rois[i].clone();
        //         overlay.add(roi);
        //     }
        //     if (overlayTemplate!=null)
        //         overlay.drawLabels(overlayTemplate.getDrawLabels());
        //     imp.setOverlay(overlay);
        //     if (imp.getCanvas()!=null)
        //         setOverlay(imp, null);
        // }
        //
        // public void mousePressed(MouseEvent e)
        // {
        //     int x = e.getX(), y = e.getY();
        //     if (e.isPopupTrigger() || e.isMetaDown())
        //         pm.show(e.getComponent(), x, y);
        // }
        //
        // public void mouseWheelMoved(MouseWheelEvent event)
        // {
        //     synchronized(this) {
        //         int index = list.getSelectedIndex();
        //         int rot = event.getWheelRotation();
        //         if (rot<-1) rot = -1;
        //         if (rot>1) rot = 1;
        //         index += rot;
        //         if (index<0) index = 0;
        //         if (index>=getCount()) index = getCount();
        //         //IJ.log(index+"  "+rot);
        //         select(index);
        //         if (IJ.isWindows())
        //             list.requestFocusInWindow();
        //         if (IJ.isJava18()&&IJ.isMacOSX())
        //             repaint();
        //     }
        // }
        //
        // /** Selects multiple ROIs, where 'indexes' is an array of integers, each
        // * greater than or equal to 0 and less than the value returned by getCount().
        // * @see #getSelectedIndexes
        // * @see #getSelectedRoisAsArray
        // * @see #getCount
        // */
        // public void setSelectedIndexes(int[] indexes)
        // {
        //     int count = getCount();
        //     if (count==0) return;
        //     for (int i = 0; i<indexes.length; i++)
        //     {
        //         if (indexes[i]<0) indexes[i]=0;
        //         if (indexes[i]>=count) indexes[i]=count-1;
        //     }
        //     list.setSelectedIndices(indexes);
        // }
        //
        /** Returns an array of the selected indexes. */
        public int[] getSelectedIndexes()
        {
            int[] res = null;

            if (InvokeRequired)
            {
                this.Invoke(() =>
                {
                    res = list.SelectedIndices.Cast<int>().ToArray();
                });
            }
            else
            {
                res = list.SelectedIndices.Cast<int>().ToArray();
            }

            return res;
        }
        //
        // /** This is a macro-callable version of getSelectedIndexes().
        //  * Example: indexes=split(call("ij.plugin.frame.RoiManager.getIndexesAsString"));
        // */
        // public static String getIndexesAsString()
        // {
        //     RoiManager rm = RoiManager.getInstance();
        //     if (rm==null) return "";
        //     String str = Arrays.toString(rm.getSelectedIndexes());
        //     str = str.replaceAll(",", "");
        //     return str.substring(1, str.length()-1);
        // }
        //
        /** Returns an array of the selected indexes or all indexes if none are selected. */
        public int[] getIndexes()
        {
            int[] indexes = getSelectedIndexes();
            if (indexes.Length==0)
                indexes = getAllIndexes();
            return indexes;
        }
        //
        // /** Returns 'true' if the index is valid and the indexed ROI is selected. */
        // public boolean isSelected(int index)
        // {
        //     return index>=0 && index<listModel.getSize() && list.isSelectedIndex(index);
        // }
        //
        // private Overlay newOverlay()
        // {
        //     Overlay overlay = OverlayLabels.createOverlay();
        //     overlay.drawLabels(labelsCheckbox.getState());
        //     if (overlay.getLabelFont()==null && overlay.getLabelColor()==null)
        //     {
        //         overlay.setLabelColor(Color.white);
        //         overlay.drawBackgrounds(true);
        //     }
        //     overlay.drawNames(Prefs.useNamesAsLabels);
        //     if (overlayTemplate!=null)
        //     {
        //         overlay.drawNames(overlayTemplate.getDrawNames());
        //         overlay.drawBackgrounds(overlayTemplate.getDrawBackgrounds());
        //         overlay.setLabelColor(overlayTemplate.getLabelColor());
        //         overlay.setLabelFont(overlayTemplate.getLabelFont(), overlayTemplate.scalableLabels());
        //     }
        //     return overlay;
        // }
        //
        // private void removeOverlay(ImagePlus imp)
        // {
        //     if (imp!=null && imp.getCanvas()!=null)
        //         setOverlay(imp, null);
        // }
        //
        // private void setOverlay(ImagePlus imp, Overlay overlay)
        // {
        //     if (imp==null)
        //         return;
        //     ImageCanvas ic = imp.getCanvas();
        //     if (ic==null)
        //     {
        //         if (imp.getOverlay()==null)
        //             imp.setOverlay(overlay);
        //         return;
        //     }
        //     ic.setShowAllList(overlay);
        //     imp.draw();
        // }
        //
        private bool record()
        {
            return Recorder.OnRecord && allowRecording && !IJ.isMacro();
        }
        //
        // private boolean recordInEvent()
        // {
        //     return Recorder.record && !IJ.isMacro();
        // }
        //
        // public void allowRecording(boolean allow)
        // {
        //     this.allowRecording = allow;
        // }
        //
        // /* handle double clicking on a ROI. */
        // public void mouseClicked(MouseEvent e)
        // {
        //     if (e.getClickCount() == 2 && !e.isConsumed())
        //     {
        //         e.consume();
        //         if (ij.plugin.MacroInstaller.isMacroCommand("ROI Manager Action"))
        //             IJ.run("ROI Manager Action");
        //     }
        // }
        //
        // public void mouseReleased(MouseEvent e) { }
        // public void mouseEntered(MouseEvent e) { }
        // public void mouseExited(MouseEvent e) { }

        private void ListOnSelectedIndexChanged(object? sender, EventArgs e)
        {
            throw new NotImplementedException();
        }

        public void valueChanged(object? sender, EventArgs e)
        {
            // if (e.getValueIsAdjusting())
            //     return;
            if (getCount()==0)
            {
                // if (recordInEvent())
                //     Recorder.record("roiManager", "Deselect");
                return;
            }
            int[] selected = list.SelectedIndices.Cast<int>().ToArray();
            if (selected.Length==0)
            {
                imageID = 0;
                return;
            }
            if (WindowManager.getCurrentImage()!=null)
            {
                if (selected.Length==1)
                {
                    ImagePlus imp = getImage();
                    if (imp!=null)
                    {
                        Roi roi = imp.getRoi();
                        if (roi!=null)
                            Roi.setPreviousRoi(roi);
                    }
                    restore(imp, selected[0], true);
                    ResultsTable.selectRow(imp!=null ? imp.getRoi() : null);
                    imageID = imp!=null ? imp.getID() : 0;
                }
                // if (recordInEvent())
                // {
                //     String arg = Arrays.toString(selected);
                //     if (!arg.startsWith("[") || !arg.endsWith("]"))
                //         return;
                //     arg = arg.substring(1, arg.length()-1);
                //     arg = arg.replace(" ", "");
                //     if (Recorder.scriptMode())
                //     {
                //         if (selected.length==1)
                //             Recorder.recordCall("rm.select("+arg+");");
                //         else
                //             Recorder.recordCall("rm.setSelectedIndexes(["+arg+"]);");
                //     }
                //     else
                //     {
                //         if (selected.length == 1)
                //             Recorder.recordString("roiManager(\"Select\", " + arg + ");\n");
                //         else
                //             Recorder.recordString("roiManager(\"Select\", newArray(" + arg + "));\n");
                //     }
                // }
            }
        }
        //
        // public void windowActivated(WindowEvent e)
        // {
        //     super.windowActivated(e);
        //     ImagePlus imp = WindowManager.getCurrentImage();
        //     if (imp!=null)
        //     {
        //         if (imageID!=0 && imp.getID()!=imageID)
        //         {
        //             showAll(SHOW_NONE);
        //             if (okToSet())
        //                 showAllCheckbox.setState(false);
        //             deselect();
        //             imageID = 0;
        //         }
        //     }
        // }
        //
        public static void resetMultiMeasureResults()
        {
            mmResults = mmResults2 = null;
        }
        //
        // public void setOverlay(Overlay overlay)
        // {
        //     if (overlay==null)
        //     {
        //         overlayTemplate = null;
        //         return;
        //     }
        //     reset();
        //     overlayTemplate = overlay.create();
        //     setEditMode(null, false);
        //     for (int i = 0; i<overlay.size(); i++)
        //         add(overlay.get(i), i+1);
        //     setEditMode(null, true);
        //     runCommand("show all");
        // }
        //
        // /** Returns the most recent I/O error message, or null if there was no error. */
        // public static String getErrorMessage()
        // {
        //     return errorMessage;
        // }
        //
        // @Override
        //     public String toString()
        // {
        //     return "RoiManager[size="+getCount()+", visible="+isVisible()+"]";
        // }
        //
        // @Override
        //     public Iterator<Roi> iterator()
        // {
        //
        //     Iterator<Roi> it = new Iterator<Roi>()
        //     {
        //
        //             private int index = -1;
        // RoiManager rm = RoiManager.getInstance();
        //
        // /** Returns 'true' if next element exists. */
        // @Override
        //             public boolean hasNext()
        // {
        //     if (index+1<rm.getCount())
        //         return true;
        //     else
        //         return false;
        // }
        //
        // /** Returns current ROI and updates pointer. */
        // @Override
        //             public Roi next()
        // {
        //     if (index+1<rm.getCount())
        //         return rm.getRoi(++index);
        //     else
        //         return null;
        // }
        //
        // @Override
        //             public void remove()
        // {
        //     throw new UnsupportedOperationException();
        // }
        //   };
        // return it;
        //  }
        //
        //  // This class runs the "Multi Measure" command in a separate thread
        //  private class MultiMeasureRunner implements Runnable
        // {
        //
        //         private Thread thread;
        // private ImagePlus imp;
        // private Roi[] rois;
        // private boolean appendResults;
        //
        // public void multiMeasure(ImagePlus imp, Roi[] rois, boolean appendResults)
        // {
        //     this.imp = imp;
        //     this.rois = rois;
        //     this.appendResults = appendResults;
        //     thread = new Thread(this, "MultiMeasure");
        //     thread.start();
        // }
        //
        // public void run()
        // {
        //     int currentSlice = imp.getCurrentSlice();
        //     ResultsTable rtMulti = RoiManager.multiMeasure(imp, rois, appendResults);
        //     mmResults = (ResultsTable)rtMulti.clone();
        //     rtMulti.show("Results");
        //     imp.setSlice(currentSlice);
        //     if (rois.length>1)
        //         IJ.run("Select None");
        // }
        //
        //  }
    }
}
